mirror of
https://github.com/vlang/v.git
synced 2025-09-09 23:39:39 -04:00
examples: add interpolated movement and input queue to snek/snek.v
(#21798)
This commit is contained in:
parent
9b6578e883
commit
dceea80d85
@ -1,9 +1,9 @@
|
|||||||
import os
|
import datatypes
|
||||||
import gg
|
import gg
|
||||||
import gx
|
import gx
|
||||||
// import sokol.sapp
|
import os
|
||||||
import time
|
|
||||||
import rand
|
import rand
|
||||||
|
import time
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
const top_height = 100
|
const top_height = 100
|
||||||
@ -11,28 +11,20 @@ const canvas_size = 700
|
|||||||
const game_size = 17
|
const game_size = 17
|
||||||
const tile_size = canvas_size / game_size
|
const tile_size = canvas_size / game_size
|
||||||
const tick_rate_ms = 100
|
const tick_rate_ms = 100
|
||||||
|
|
||||||
const high_score_file_path = os.join_path(os.cache_dir(), 'v', 'examples', 'snek')
|
const high_score_file_path = os.join_path(os.cache_dir(), 'v', 'examples', 'snek')
|
||||||
|
|
||||||
// types
|
// types
|
||||||
struct Pos {
|
struct Vec {
|
||||||
x int
|
x int
|
||||||
y int
|
y int
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (a Pos) + (b Pos) Pos {
|
fn (a Vec) + (b Vec) Vec {
|
||||||
return Pos{a.x + b.x, a.y + b.y}
|
return Vec{a.x + b.x, a.y + b.y}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (a Pos) - (b Pos) Pos {
|
fn (a Vec) - (b Vec) Vec {
|
||||||
return Pos{a.x - b.x, a.y - b.y}
|
return Vec{a.x - b.x, a.y - b.y}
|
||||||
}
|
|
||||||
|
|
||||||
enum Direction {
|
|
||||||
up
|
|
||||||
down
|
|
||||||
left
|
|
||||||
right
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HighScore = int
|
type HighScore = int
|
||||||
@ -48,30 +40,28 @@ fn (mut h HighScore) load() {
|
|||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
mut:
|
mut:
|
||||||
gg &gg.Context = unsafe { nil }
|
gg &gg.Context = unsafe { nil }
|
||||||
score int
|
score int
|
||||||
best HighScore
|
best HighScore
|
||||||
snake []Pos
|
snake []Vec
|
||||||
dir Direction
|
dir Vec
|
||||||
last_dir Direction
|
dir_queue datatypes.Queue[Vec]
|
||||||
food Pos
|
food Vec
|
||||||
start_time i64
|
last_tick i64
|
||||||
last_tick i64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility
|
// utility
|
||||||
fn (mut app App) reset_game() {
|
fn (mut app App) reset_game() {
|
||||||
app.score = 0
|
app.score = 0
|
||||||
app.snake = [
|
app.snake = [
|
||||||
Pos{3, 8},
|
Vec{3, 8},
|
||||||
Pos{2, 8},
|
Vec{2, 8},
|
||||||
Pos{1, 8},
|
Vec{1, 8},
|
||||||
Pos{0, 8},
|
Vec{0, 8},
|
||||||
]
|
]
|
||||||
app.dir = .right
|
app.dir = Vec{1, 0}
|
||||||
app.last_dir = app.dir
|
app.dir_queue = datatypes.Queue[Vec]{}
|
||||||
app.food = Pos{10, 8}
|
app.food = Vec{10, 8}
|
||||||
app.start_time = time.ticks()
|
|
||||||
app.last_tick = time.ticks()
|
app.last_tick = time.ticks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +69,7 @@ fn (mut app App) move_food() {
|
|||||||
for {
|
for {
|
||||||
x := rand.intn(game_size) or { 0 }
|
x := rand.intn(game_size) or { 0 }
|
||||||
y := rand.intn(game_size) or { 0 }
|
y := rand.intn(game_size) or { 0 }
|
||||||
app.food = Pos{x, y}
|
app.food = Vec{x, y}
|
||||||
|
|
||||||
if app.food !in app.snake {
|
if app.food !in app.snake {
|
||||||
return
|
return
|
||||||
@ -89,81 +79,65 @@ fn (mut app App) move_food() {
|
|||||||
|
|
||||||
// events
|
// events
|
||||||
fn on_keydown(key gg.KeyCode, mod gg.Modifier, mut app App) {
|
fn on_keydown(key gg.KeyCode, mod gg.Modifier, mut app App) {
|
||||||
match key {
|
dir := match key {
|
||||||
.w, .up {
|
.w, .up {
|
||||||
if app.last_dir != .down {
|
Vec{0, -1}
|
||||||
app.dir = .up
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.s, .down {
|
.s, .down {
|
||||||
if app.last_dir != .up {
|
Vec{0, 1}
|
||||||
app.dir = .down
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.a, .left {
|
.a, .left {
|
||||||
if app.last_dir != .right {
|
Vec{-1, 0}
|
||||||
app.dir = .left
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.d, .right {
|
.d, .right {
|
||||||
if app.last_dir != .left {
|
Vec{1, 0}
|
||||||
app.dir = .right
|
}
|
||||||
}
|
else {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
else {}
|
|
||||||
}
|
}
|
||||||
|
app.dir_queue.push(dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_frame(mut app App) {
|
fn on_frame(mut app App) {
|
||||||
|
// check if snake bit itself
|
||||||
|
if app.snake[0] in app.snake[1..] {
|
||||||
|
app.reset_game()
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if snake hit a wall
|
||||||
|
if app.snake[0].x < 0 || app.snake[0].x >= game_size || app.snake[0].y < 0
|
||||||
|
|| app.snake[0].y >= game_size {
|
||||||
|
app.reset_game()
|
||||||
|
}
|
||||||
|
|
||||||
|
progress := f32_min(1, f32(time.ticks() - app.last_tick) / f32(tick_rate_ms))
|
||||||
app.gg.begin()
|
app.gg.begin()
|
||||||
|
|
||||||
now := time.ticks()
|
// draw food
|
||||||
|
app.gg.draw_rect_filled(tile_size * app.food.x, tile_size * app.food.y + top_height,
|
||||||
|
tile_size, tile_size, gx.red)
|
||||||
|
|
||||||
if now - app.last_tick >= tick_rate_ms {
|
// draw snake
|
||||||
app.last_tick = now
|
for pos in app.snake[..app.snake.len - 1] {
|
||||||
|
|
||||||
// finding delta direction
|
|
||||||
delta_dir := match app.dir {
|
|
||||||
.up { Pos{0, -1} }
|
|
||||||
.down { Pos{0, 1} }
|
|
||||||
.left { Pos{-1, 0} }
|
|
||||||
.right { Pos{1, 0} }
|
|
||||||
}
|
|
||||||
|
|
||||||
// "snaking" along
|
|
||||||
mut prev := app.snake[0]
|
|
||||||
app.snake[0] = app.snake[0] + delta_dir
|
|
||||||
|
|
||||||
for i in 1 .. app.snake.len {
|
|
||||||
tmp := app.snake[i]
|
|
||||||
app.snake[i] = prev
|
|
||||||
prev = tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
// adding last segment
|
|
||||||
if app.snake[0] == app.food {
|
|
||||||
app.move_food()
|
|
||||||
app.score++
|
|
||||||
if app.score > app.best {
|
|
||||||
app.best = app.score
|
|
||||||
app.best.save()
|
|
||||||
}
|
|
||||||
app.snake << app.snake.last() + app.snake.last() - app.snake[app.snake.len - 2]
|
|
||||||
}
|
|
||||||
|
|
||||||
app.last_dir = app.dir
|
|
||||||
}
|
|
||||||
// drawing snake
|
|
||||||
for pos in app.snake {
|
|
||||||
app.gg.draw_rect_filled(tile_size * pos.x, tile_size * pos.y + top_height, tile_size,
|
app.gg.draw_rect_filled(tile_size * pos.x, tile_size * pos.y + top_height, tile_size,
|
||||||
tile_size, gx.blue)
|
tile_size, gx.blue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawing food
|
// draw partial head
|
||||||
app.gg.draw_rect_filled(tile_size * app.food.x, tile_size * app.food.y + top_height,
|
head := app.snake[0]
|
||||||
tile_size, tile_size, gx.red)
|
app.gg.draw_rect_filled(int(tile_size * (head.x + app.dir.x * progress)),
|
||||||
|
int(tile_size * (head.y + app.dir.y * progress)) + top_height, tile_size, tile_size,
|
||||||
|
gx.blue)
|
||||||
|
|
||||||
// drawing top
|
// draw partial tail
|
||||||
|
tail := app.snake.last()
|
||||||
|
tail_dir := app.snake[app.snake.len - 2] - tail
|
||||||
|
app.gg.draw_rect_filled(int(tile_size * (tail.x + tail_dir.x * progress)),
|
||||||
|
int(tile_size * (tail.y + tail_dir.y * progress)) + top_height, tile_size, tile_size,
|
||||||
|
gx.blue)
|
||||||
|
|
||||||
|
// draw score bar
|
||||||
app.gg.draw_rect_filled(0, 0, canvas_size, top_height, gx.black)
|
app.gg.draw_rect_filled(0, 0, canvas_size, top_height, gx.black)
|
||||||
app.gg.draw_text(150, top_height / 2, 'Score: ${app.score}', gx.TextCfg{
|
app.gg.draw_text(150, top_height / 2, 'Score: ${app.score}', gx.TextCfg{
|
||||||
color: gx.white
|
color: gx.white
|
||||||
@ -178,14 +152,35 @@ fn on_frame(mut app App) {
|
|||||||
size: 65
|
size: 65
|
||||||
})
|
})
|
||||||
|
|
||||||
// checking if snake bit itself
|
if progress == 1 {
|
||||||
if app.snake[0] in app.snake[1..] {
|
// "snake" along
|
||||||
app.reset_game()
|
mut prev := app.snake[0]
|
||||||
}
|
app.snake[0] = app.snake[0] + app.dir
|
||||||
// checking if snake hit a wall
|
|
||||||
if app.snake[0].x < 0 || app.snake[0].x >= game_size || app.snake[0].y < 0
|
for i in 1 .. app.snake.len {
|
||||||
|| app.snake[0].y >= game_size {
|
tmp := app.snake[i]
|
||||||
app.reset_game()
|
app.snake[i] = prev
|
||||||
|
prev = tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// add tail segment if food has been eaten
|
||||||
|
if app.snake[0] == app.food {
|
||||||
|
app.score++
|
||||||
|
if app.score > app.best {
|
||||||
|
app.best = app.score
|
||||||
|
app.best.save()
|
||||||
|
}
|
||||||
|
app.snake << app.snake.last() + app.snake.last() - app.snake[app.snake.len - 2]
|
||||||
|
app.move_food()
|
||||||
|
}
|
||||||
|
|
||||||
|
if dir := app.dir_queue.pop() {
|
||||||
|
if dir.x != -app.dir.x || dir.y != -app.dir.y {
|
||||||
|
app.dir = dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.last_tick = time.ticks()
|
||||||
}
|
}
|
||||||
|
|
||||||
app.gg.end()
|
app.gg.end()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user