Input and Update with sum type

This commit is contained in:
Joseph Ferano 2024-08-25 18:32:49 +07:00
parent 6d9dbfc5f8
commit 1ec3c577fe

250
game.odin
View File

@ -9,8 +9,11 @@ Rect :: rl.Rectangle
SCREEN_WIDTH : i32 = 1000
SCREEN_HEIGHT : i32 = 800
BW := f32(SCREEN_WIDTH)
BH := f32(SCREEN_HEIGHT)
SCALE: f32 = 0.5
SCALE: f32 = 0.37
SHIP_W: f32 = 70 * SCALE
SHIP_H: f32 = 100 * SCALE
@ -31,21 +34,42 @@ Asteroid :: struct {
rot: f32,
}
GameState :: struct {
dt: f32,
ship_pos: Vec2,
ship_angle: f32,
ship_vel: Vec2,
bullets: [dynamic]Bullet,
asteroids: [dynamic]Asteroid,
Player :: struct {
pos: Vec2,
points: [4]Vec2,
angle: f32,
vel: Vec2
}
get_ship_shape :: proc(s : GameState) -> (Vec2, Vec2, Vec2, Vec2) {
v1 := rl.Vector2Rotate({ SHIP_H*0.5, 0}, s.ship_angle) + s.ship_pos
v2 := rl.Vector2Rotate({-SHIP_H*0.5, -SHIP_W*0.5}, s.ship_angle) + s.ship_pos
v3 := rl.Vector2Rotate({-SHIP_H*0.5*0.5, 0}, s.ship_angle) + s.ship_pos
v4 := rl.Vector2Rotate({-SHIP_H*0.5, SHIP_W*0.5}, s.ship_angle) + s.ship_pos
return v1, v2, v3, v4
PlayerSpinningPart :: struct {
points: [2]Vec2,
rotation_speed: f32,
vel: Vec2,
}
Death :: struct {
flying_parts: [4]PlayerSpinningPart
}
PlayerState :: union #no_nil {
Player,
Death,
}
GameState :: struct {
dt: f32,
player_state: PlayerState,
bullets: [dynamic]Bullet,
asteroids: [dynamic]Asteroid,
bullet_pop_idxs: [dynamic]int,
asteroid_pop_idxs: [dynamic]int,
}
update_ship_shape :: proc(p : ^Player) {
p.points[0] = rl.Vector2Rotate({ SHIP_H*0.5, 0}, p.angle) + p.pos
p.points[1] = rl.Vector2Rotate({-SHIP_H*0.5, -SHIP_W*0.5}, p.angle) + p.pos
p.points[2] = rl.Vector2Rotate({-SHIP_H*0.5*0.5, 0}, p.angle) + p.pos
p.points[3] = rl.Vector2Rotate({-SHIP_H*0.5, SHIP_W*0.5}, p.angle) + p.pos
}
swap_n_pop :: proc(bullets: ^[dynamic]$T, idx: int) {
@ -58,25 +82,127 @@ swap_n_pop :: proc(bullets: ^[dynamic]$T, idx: int) {
}
}
player_input :: proc(s: ^GameState) {
switch &player in s.player_state {
case Player:
if rl.IsKeyDown(.D) {
player.angle += ANGULAR_SPEED * s.dt
}
if rl.IsKeyDown(.A) {
player.angle -= ANGULAR_SPEED * s.dt
}
if rl.IsKeyDown(.W) {
player.vel.x += math.cos(player.angle) * THRUST_SPEED * s.dt
player.vel.y += math.sin(player.angle) * THRUST_SPEED * s.dt
}
if rl.IsKeyPressed(.SPACE) {
b_vel := Vec2{ math.cos(player.angle) , math.sin(player.angle) } * BULLET_SPEED
append(&s.bullets, Bullet{player.points[0], b_vel})
}
case Death:
if rl.IsKeyDown(.ENTER) {
// restart game
}
}
}
update :: proc(s: ^GameState) {
switch &player in s.player_state {
case Player:
update_ship_shape(&player)
player.pos += player.vel * (5000 * s.dt)
ship_collision := false
for i := 0; i < len(s.asteroids); i += 1 {
s.asteroids[i].rect.x += s.asteroids[i].vel.x
s.asteroids[i].rect.y += s.asteroids[i].vel.y
for j := 0; j < len(player.points); j += 1 {
if rl.CheckCollisionPointRec(player.points[j], s.asteroids[i].rect) {
ship_collision = true
}
}
}
for i := 0; i < len(s.bullets); i += 1 {
if ( s.bullets[i].pos.x < 0 || s.bullets[i].pos.x > BW
|| s.bullets[i].pos.y < 0 || s.bullets[i].pos.y > BH) {
append(&s.bullet_pop_idxs, i)
}
s.bullets[i].pos += s.bullets[i].vel
for j := 0; j < len(s.asteroids); j += 1 {
if rl.CheckCollisionCircleRec(s.bullets[i].pos, BULLET_RADIUS, s.asteroids[j].rect) {
append(&s.bullet_pop_idxs, i)
append(&s.asteroid_pop_idxs, j)
}
}
}
bpi_len := len(s.bullet_pop_idxs)
for i := 0; i < bpi_len; i += 1 {
swap_n_pop(&s.bullets, s.bullet_pop_idxs[i])
}
if bpi_len > 0 {
clear(&s.bullet_pop_idxs)
}
api_len := len(s.asteroid_pop_idxs)
for i := 0; i < api_len; i += 1 {
swap_n_pop(&s.asteroids, s.asteroid_pop_idxs[i])
}
if bpi_len > 0 {
clear(&s.asteroid_pop_idxs)
}
if ship_collision {
// Randomize everything
s.player_state = Death {
}
} else {
if player.pos.x + SHIP_W < 0 { player.pos.x = BW + SHIP_W}
if player.pos.x - SHIP_W > BW { player.pos.x = -SHIP_W}
if player.pos.y + SHIP_H < 0 { player.pos.y = BH + SHIP_H}
if player.pos.y - SHIP_H > BH { player.pos.y = -SHIP_H}
}
case Death:
}
}
draw2d :: proc(s: ^GameState) {
switch &player in s.player_state {
case Player:
// Draw ship
rl.DrawLineEx(player.points[0], player.points[1], 1, rl.WHITE)
rl.DrawLineEx(player.points[1], player.points[2], 1, rl.WHITE)
rl.DrawLineEx(player.points[2], player.points[3], 1, rl.WHITE)
rl.DrawLineEx(player.points[3], player.points[0], 1, rl.WHITE)
case Death:
}
for i := 0; i < len(s.asteroids); i += 1 {
rl.DrawRectangleLinesEx(s.asteroids[i].rect, 1, rl.WHITE)
}
for i := 0; i < len(s.bullets); i += 1 {
rl.DrawCircleLinesV(s.bullets[i].pos, BULLET_RADIUS, rl.WHITE)
}
}
main :: proc() {
rl.SetTraceLogLevel(.ERROR)
rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Asteroids")
start_pos := Vec2{BW / 2 - SHIP_W / 2, BH / 2 - SHIP_H / 2}
s := GameState{
rl.GetFrameTime(),
{0,0},
-math.PI / 2,
{0,0},
Player {
start_pos,
[4]Vec2{{0,0},{0,0},{0,0},{0,0}},
-math.PI / 2,
{0,0}
},
make([dynamic]Bullet, 0, 64),
make([dynamic]Asteroid, 0, 64),
make([dynamic]int, 0, 64),
make([dynamic]int, 0, 64),
}
s.ship_pos = {600, 350}
bw := f32(SCREEN_WIDTH)
bh := f32(SCREEN_HEIGHT)
bullet_pop_idxs := make([dynamic]int, 0, 64)
asteroid_pop_idxs := make([dynamic]int, 0, 64)
get_rand_angle :: proc(min: i32, max: i32) -> f32 {
return f32(rl.GetRandomValue(min, max)) * rl.DEG2RAD
@ -84,7 +210,7 @@ main :: proc() {
for i := 0; i < 5; i += 1 {
rand_angle := get_rand_angle(-50, 50)
rand_pos := Vec2{ f32(rl.GetRandomValue(0, i32(bw))), -50 }
rand_pos := Vec2{ f32(rl.GetRandomValue(0, i32(BW))), -50 }
rand_size := f32(rl.GetRandomValue(25, 60))
rand_vy := f32(rl.GetRandomValue(1, 2)) * 0.5
asteroid := Asteroid {
@ -98,82 +224,14 @@ main :: proc() {
for !rl.WindowShouldClose() {
s.dt = rl.GetFrameTime()
v1, v2, v3, v4 := get_ship_shape(s)
player_input(&s)
if rl.IsKeyDown(.D) {
s.ship_angle += ANGULAR_SPEED * s.dt
}
if rl.IsKeyDown(.A) {
s.ship_angle -= ANGULAR_SPEED * s.dt
}
if rl.IsKeyDown(.W) {
s.ship_vel.x += math.cos(s.ship_angle) * THRUST_SPEED * s.dt
s.ship_vel.y += math.sin(s.ship_angle) * THRUST_SPEED * s.dt
}
if rl.IsKeyPressed(.SPACE) {
b_vel := Vec2{ math.cos(s.ship_angle) , math.sin(s.ship_angle) } * BULLET_SPEED
append(&s.bullets, Bullet{v1,b_vel})
}
s.ship_pos += s.ship_vel * (5000 * s.dt)
for i := 0; i < len(s.asteroids); i += 1 {
s.asteroids[i].rect.x += s.asteroids[i].vel.x
s.asteroids[i].rect.y += s.asteroids[i].vel.y
}
for i := 0; i < len(s.bullets); i += 1 {
if ( s.bullets[i].pos.x < 0 || s.bullets[i].pos.x > bw
|| s.bullets[i].pos.y < 0 || s.bullets[i].pos.y > bh) {
append(&bullet_pop_idxs, i)
}
s.bullets[i].pos += s.bullets[i].vel
for j := 0; j < len(s.asteroids); j += 1 {
if rl.CheckCollisionCircleRec(s.bullets[i].pos, BULLET_RADIUS, s.asteroids[j].rect) {
fmt.println(s.bullets[i], s.asteroids[j])
append(&bullet_pop_idxs, i)
append(&asteroid_pop_idxs, j)
}
}
}
bpi_len := len(bullet_pop_idxs)
for i := 0; i < bpi_len; i += 1 {
swap_n_pop(&s.bullets, bullet_pop_idxs[i])
}
if bpi_len > 0 {
clear(&bullet_pop_idxs)
}
api_len := len(asteroid_pop_idxs)
for i := 0; i < api_len; i += 1 {
swap_n_pop(&s.asteroids, asteroid_pop_idxs[i])
}
if bpi_len > 0 {
clear(&asteroid_pop_idxs)
}
if s.ship_pos.x + SHIP_W < 0 { s.ship_pos.x = bw + SHIP_W}
if s.ship_pos.x - SHIP_W > bw { s.ship_pos.x = -SHIP_W}
if s.ship_pos.y + SHIP_H < 0 { s.ship_pos.y = bh + SHIP_H}
if s.ship_pos.y - SHIP_H > bh { s.ship_pos.y = -SHIP_H}
update(&s)
rl.BeginDrawing()
rl.ClearBackground(rl.BLACK)
// Draw ship
rl.DrawLineEx(v1, v2, 1, rl.WHITE)
rl.DrawLineEx(v2, v3, 1, rl.WHITE)
rl.DrawLineEx(v3, v4, 1, rl.WHITE)
rl.DrawLineEx(v4, v1, 1, rl.WHITE)
for i := 0; i < len(s.asteroids); i += 1 {
rl.DrawRectangleLinesEx(s.asteroids[i].rect, 1, rl.WHITE)
}
for i := 0; i < len(s.bullets); i += 1 {
rl.DrawCircleLinesV(s.bullets[i].pos, BULLET_RADIUS, rl.WHITE)
}
draw2d(&s)
rl.EndDrawing()
}