Initial enemy setup, with collisions and basic spawning.
Modified-by: Jason Hilder <jhilder95@gmail.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
+19
-1
@@ -12,13 +12,19 @@ Player_Assets :: struct {
|
||||
clips: [Player_State]AnimationClip,
|
||||
}
|
||||
|
||||
Enemy_Assets :: struct {
|
||||
clips: [Enemy_State]AnimationClip,
|
||||
}
|
||||
|
||||
Asset_Manager :: struct {
|
||||
player: Player_Assets,
|
||||
enemy: Enemy_Assets,
|
||||
}
|
||||
|
||||
asset_manager_init :: proc() -> Asset_Manager {
|
||||
assets := Asset_Manager{}
|
||||
init_player_assets(&assets.player.clips);
|
||||
init_player_assets(&assets.player.clips)
|
||||
init_enemy_assets(&assets.enemy.clips)
|
||||
|
||||
return assets
|
||||
}
|
||||
@@ -35,6 +41,18 @@ init_player_assets :: proc(clip_array: ^[Player_State]AnimationClip) {
|
||||
}
|
||||
}
|
||||
|
||||
init_enemy_assets :: proc(clip_array: ^[Enemy_State]AnimationClip) {
|
||||
idle_tex := rl.LoadTexture("assets/textures/enemy/player_idle.png")
|
||||
walk_tex := rl.LoadTexture("assets/textures/enemy/player_walk.png")
|
||||
|
||||
clip_array[.IDLE] = AnimationClip {
|
||||
texture = idle_tex, frame_count = 6, frame_width = 32, frame_height = 32, frame_time = 0.14, looping = true,
|
||||
}
|
||||
clip_array[.WALK] = AnimationClip {
|
||||
texture = walk_tex, frame_count = 8, frame_width = 32, frame_height = 32, frame_time = 0.14, looping = true,
|
||||
}
|
||||
}
|
||||
|
||||
asset_manager_destroy :: proc(assets: ^Asset_Manager) {
|
||||
for clip in assets.player.clips {
|
||||
rl.UnloadTexture(clip.texture)
|
||||
|
||||
@@ -22,8 +22,16 @@ debug_init :: proc(game: ^Game) {
|
||||
debug_fps :: proc() {
|
||||
when DEBUG_MODE {
|
||||
rl.DrawFPS(20, 20)
|
||||
for i in i32(0)..<4 {
|
||||
if rl.IsGamepadAvailable(i) {
|
||||
rl.DrawText(rl.TextFormat("GP%d: %s", i, rl.GetGamepadName(i)), 20, 45 + i*20, 20, rl.BLACK)
|
||||
} else {
|
||||
rl.DrawText(rl.TextFormat("GP%d: NOT DETECTED", i), 20, 45 + i*20, 20, rl.GRAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DEBUG CAMERA
|
||||
debug_camera_init :: proc(player: Player) -> rl.Camera2D {
|
||||
@@ -82,6 +90,13 @@ debug_bounds :: proc(game: ^Game) {
|
||||
player_collider := get_player_collider(&game.player, game.player.position)
|
||||
rl.DrawRectangleLines( i32(player.position.x), i32(player.position.y), TILE_SIZE, TILE_SIZE, rl.GREEN)
|
||||
rl.DrawRectangleLinesEx(player_collider, 1, rl.BLUE)
|
||||
|
||||
// enemies
|
||||
for &e in game.enemies {
|
||||
enemy_collider := get_enemy_collider(e, e.position)
|
||||
rl.DrawRectangleLinesEx(enemy_collider, 1, rl.BLACK)
|
||||
rl.DrawRectangleLines( i32(e.position.x), i32(e.position.y), TILE_SIZE, TILE_SIZE, rl.MAROON)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+100
-9
@@ -1,24 +1,115 @@
|
||||
package game
|
||||
|
||||
import "core:math/rand"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Enemy :: struct {
|
||||
position: rl.Vector2,
|
||||
Enemy_State :: enum { IDLE, WALK, ATTACK }
|
||||
Enemy_Type :: enum { SKELETON, ZOMBIE, GHOST }
|
||||
|
||||
Enemy :: struct {
|
||||
// info
|
||||
type: Enemy_Type,
|
||||
position: rl.Vector2,
|
||||
state: Enemy_State,
|
||||
animation: AnimationState,
|
||||
flipped: bool,
|
||||
|
||||
// movement
|
||||
velocity: rl.Vector2,
|
||||
speed: f32, // max move speed
|
||||
speed: f32,
|
||||
|
||||
// collision
|
||||
collider_offset: rl.Vector2,
|
||||
collider_w: f32,
|
||||
collider_h: f32
|
||||
}
|
||||
|
||||
enemy_init :: proc() -> Enemy {
|
||||
enemy := Enemy{}
|
||||
|
||||
enemy.position = {300, 300}
|
||||
enemy_init :: proc(assets: ^Enemy_Assets, pos: [2]f32) -> Enemy {
|
||||
enemy := Enemy{}
|
||||
enemy.position = {pos.x, pos.y}
|
||||
enemy.state = .IDLE
|
||||
enemy.collider_offset = rl.Vector2{8, 15}
|
||||
enemy.collider_w = 16
|
||||
enemy.collider_h = 10
|
||||
animation_play(&enemy.animation, &assets.clips[.IDLE])
|
||||
|
||||
return enemy
|
||||
}
|
||||
|
||||
update_enemy :: proc() { }
|
||||
get_enemy_collider :: proc(enemy: Enemy, pos: rl.Vector2) -> rl.Rectangle {
|
||||
return rl.Rectangle{
|
||||
(pos.x + enemy.collider_offset.x),
|
||||
pos.y + enemy.collider_offset.y,
|
||||
enemy.collider_w,
|
||||
enemy.collider_h,
|
||||
}
|
||||
}
|
||||
|
||||
draw_enemy :: proc(enemy: Enemy) {
|
||||
rl.DrawCircle(i32(enemy.position.x), i32(enemy.position.y), 10, rl.RED)
|
||||
get_spawn_rect :: proc(max_x, max_y: f32) -> rl.Rectangle {
|
||||
// Get random x and y pos
|
||||
x_pos := rand.float32_range(TILE_SIZE, (max_x - TILE_SIZE))
|
||||
y_pos := rand.float32_range(TILE_SIZE, (max_y - TILE_SIZE))
|
||||
|
||||
return rl.Rectangle{x_pos, y_pos, TILE_SIZE, TILE_SIZE}
|
||||
}
|
||||
|
||||
// @TODO consider a self check, ie pass the enemy in question index and make sure to skip that
|
||||
spawn_enemy :: proc(enemy_array: ^[MAX_ENEMIES]Enemy, assets: ^Enemy_Assets, enemy_count: ^int, game_map: Game_Map) {
|
||||
max_x := game_map.width_px
|
||||
max_y := game_map.height_px
|
||||
cnt := enemy_count^
|
||||
|
||||
// Can only have Max amount of enemies
|
||||
if cnt < MAX_ENEMIES {
|
||||
|
||||
MAX_ATTEMPTS :: 30
|
||||
enemy_rect := rl.Rectangle{}
|
||||
valid_pos := false
|
||||
|
||||
for attempt := 0; !valid_pos && attempt < MAX_ATTEMPTS; attempt += 1 {
|
||||
enemy_rect = get_spawn_rect(max_x, max_y)
|
||||
valid_pos = !check_map_collision(enemy_rect, game_map) && !check_enemy_collision(enemy_rect, cnt, enemy_array)
|
||||
}
|
||||
if !valid_pos {
|
||||
return // couldn't find a spot this call, skip spawning
|
||||
}
|
||||
|
||||
// init the enemy there in .idle state
|
||||
enemy_array[cnt] = enemy_init(assets, {enemy_rect.x, enemy_rect.y})
|
||||
cnt += 1
|
||||
enemy_count^ = cnt
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
update_enemies :: proc(enemy_array: ^[MAX_ENEMIES]Enemy, assets: ^Enemy_Assets) {
|
||||
for &enemy in enemy_array {
|
||||
moving := false
|
||||
dt := rl.GetFrameTime()
|
||||
|
||||
|
||||
enemy.state = moving ? .WALK : .IDLE
|
||||
animation_play(&enemy.animation, &assets.clips[enemy.state])
|
||||
animation_update(&enemy.animation, dt)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
draw_enemies :: proc(enemy_array: ^[MAX_ENEMIES]Enemy) {
|
||||
for &enemy in enemy_array {
|
||||
clip := enemy.animation.clip
|
||||
src_rect := animation_frame_rect(&enemy.animation)
|
||||
|
||||
dst_rect := rl.Rectangle{
|
||||
x = enemy.position.x,
|
||||
y = enemy.position.y,
|
||||
width = f32(clip.frame_width),
|
||||
height = f32(clip.frame_height),
|
||||
}
|
||||
|
||||
rl.DrawTexturePro(clip.texture, src_rect, dst_rect, rl.Vector2{}, 0, rl.WHITE)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -7,14 +7,15 @@ import "core:mem"
|
||||
// -----------------------------------------------------------------------------
|
||||
DEBUG_MODE :: true
|
||||
|
||||
WINDOW_TITLE :: "Graveyard"
|
||||
WINDOW_WIDTH :: 1920
|
||||
WINDOW_HEIGHT :: 1080
|
||||
GAME_WIDTH :: 480
|
||||
GAME_HEIGHT :: 270
|
||||
|
||||
WINDOW_TITLE :: "Graveyard"
|
||||
PLAYER_SPEED :: 180
|
||||
MAX_ENEMIES :: 140
|
||||
MAX_PROPS :: 64
|
||||
TILE_SIZE :: 32
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
main :: proc() {
|
||||
|
||||
@@ -171,6 +171,21 @@ check_map_collision :: proc(rect: rl.Rectangle, game_map: Game_Map) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// true when collision found
|
||||
check_enemy_collision :: proc(rect_a: rl.Rectangle, active_enemies: int, enemies: ^[MAX_ENEMIES]Enemy) -> bool {
|
||||
|
||||
for idx in 0..<active_enemies {
|
||||
e := enemies[idx]
|
||||
enemy_rect := get_enemy_collider(e, e.position)
|
||||
|
||||
if rl.CheckCollisionRecs(rect_a, enemy_rect) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
draw_map :: proc(current_map: Game_Map) {
|
||||
for value, index in current_map.data {
|
||||
row := index / current_map.width
|
||||
|
||||
Reference in New Issue
Block a user