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,
|
clips: [Player_State]AnimationClip,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Enemy_Assets :: struct {
|
||||||
|
clips: [Enemy_State]AnimationClip,
|
||||||
|
}
|
||||||
|
|
||||||
Asset_Manager :: struct {
|
Asset_Manager :: struct {
|
||||||
player: Player_Assets,
|
player: Player_Assets,
|
||||||
|
enemy: Enemy_Assets,
|
||||||
}
|
}
|
||||||
|
|
||||||
asset_manager_init :: proc() -> Asset_Manager {
|
asset_manager_init :: proc() -> Asset_Manager {
|
||||||
assets := 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
|
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) {
|
asset_manager_destroy :: proc(assets: ^Asset_Manager) {
|
||||||
for clip in assets.player.clips {
|
for clip in assets.player.clips {
|
||||||
rl.UnloadTexture(clip.texture)
|
rl.UnloadTexture(clip.texture)
|
||||||
|
|||||||
@@ -22,7 +22,15 @@ debug_init :: proc(game: ^Game) {
|
|||||||
debug_fps :: proc() {
|
debug_fps :: proc() {
|
||||||
when DEBUG_MODE {
|
when DEBUG_MODE {
|
||||||
rl.DrawFPS(20, 20)
|
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
|
||||||
@@ -82,6 +90,13 @@ debug_bounds :: proc(game: ^Game) {
|
|||||||
player_collider := get_player_collider(&game.player, game.player.position)
|
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.DrawRectangleLines( i32(player.position.x), i32(player.position.y), TILE_SIZE, TILE_SIZE, rl.GREEN)
|
||||||
rl.DrawRectangleLinesEx(player_collider, 1, rl.BLUE)
|
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
|
package game
|
||||||
|
|
||||||
|
import "core:math/rand"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
Enemy :: struct {
|
Enemy_State :: enum { IDLE, WALK, ATTACK }
|
||||||
position: rl.Vector2,
|
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,
|
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
|
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) {
|
get_spawn_rect :: proc(max_x, max_y: f32) -> rl.Rectangle {
|
||||||
rl.DrawCircle(i32(enemy.position.x), i32(enemy.position.y), 10, rl.RED)
|
// 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
|
DEBUG_MODE :: true
|
||||||
|
|
||||||
|
WINDOW_TITLE :: "Graveyard"
|
||||||
WINDOW_WIDTH :: 1920
|
WINDOW_WIDTH :: 1920
|
||||||
WINDOW_HEIGHT :: 1080
|
WINDOW_HEIGHT :: 1080
|
||||||
GAME_WIDTH :: 480
|
GAME_WIDTH :: 480
|
||||||
GAME_HEIGHT :: 270
|
GAME_HEIGHT :: 270
|
||||||
|
MAX_ENEMIES :: 140
|
||||||
WINDOW_TITLE :: "Graveyard"
|
MAX_PROPS :: 64
|
||||||
PLAYER_SPEED :: 180
|
|
||||||
TILE_SIZE :: 32
|
TILE_SIZE :: 32
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
|
|||||||
@@ -171,6 +171,21 @@ check_map_collision :: proc(rect: rl.Rectangle, game_map: Game_Map) -> bool {
|
|||||||
return false
|
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) {
|
draw_map :: proc(current_map: Game_Map) {
|
||||||
for value, index in current_map.data {
|
for value, index in current_map.data {
|
||||||
row := index / current_map.width
|
row := index / current_map.width
|
||||||
|
|||||||
Reference in New Issue
Block a user