Я вже писав мінімальний огляд функціоналу бібліотеки gosu. Щоб написати просту 2D гру насправді непотрібно багато коду. Також можна уникнути графічних елементів та використовувати прості фігури (програмно намальовані). Для початку (під час вивчення рушія) так і треба робити, але стрибаючий прямокутник може бути не таким прикольним, як щось намальоване.
Я взяв безкоштовні іконки (атрибуція обов'язкова за умовам використання) Freepik на сайті Flaticon. Графіку можна знайти повністю безкоштовну, намалювати самостійно, замовити або ж згенерувати за допомогою ШІ. Генерований контент може підійти для процесу розробки, а для релізу у світ всеж таки краще скористатись послугами художників та підтримати економіку та їх творчість.
Ну тож повернемося до теми цього допису - демки гри Drones vs Zombies. Концепція доволі проста - за допомогою дрона потрібно знищувати зомбі, які намагаються дістатись до пілота. Демка не ідеальна, але гарно демонструє, що приблизно 250 строк коду та декілька картинок можуть перетворити ідею/концепцію гри у демку.
Сам код моєї демки:
# Drones VS Zombies v.1.0.0
# Icons: https://www.flaticon.com/authors/freepik
# Code: demo by memecode https://tseivo.com/b/memecode
require 'gosu'
class DroneGame < Gosu::Window
WIDTH = 800
HEIGHT = 600
def initialize
super(WIDTH, HEIGHT)
self.caption = "Drones VS Zombies"
reset_game
@background_color = Gosu::Color.new(255, 85, 104, 50)
@font = Gosu::Font.new(20)
@paused = false
end
def update
return if @game_over || @paused
@drones.each(&:update)
handle_explosions
@drones.reject!(&:exploded?)
spawn_drone if @drones.empty?
@zombies.each(&:update)
handle_zombie_movement
handle_collisions
@zombies.reject!(&:dead?)
spawn_zombie if rand < 0.02
check_game_over
end
def draw
draw_quad(0, 0, @background_color, WIDTH, 0, @background_color, WIDTH, HEIGHT, @background_color, 0, HEIGHT, @background_color)
@operator.draw
@drones.each(&:draw)
@zombies.each(&:draw)
@font.draw_text("Points: #{@points}", 10, 10, 2)
if @game_over
@font.draw_text("Game Over! Final Score: #{@points}", WIDTH / 2 - 100, HEIGHT / 2, 3, 1.0, 1.0, Gosu::Color::RED)
@font.draw_text("Press Space to Restart", WIDTH / 2 - 100, HEIGHT / 2 + 30, 3, 1.0, 1.0, Gosu::Color::WHITE)
elsif @paused
@font.draw_text("Paused", WIDTH / 2 - 50, HEIGHT / 2, 3, 1.0, 1.0, Gosu::Color::YELLOW)
@font.draw_text("Press Esc to Resume", WIDTH / 2 - 100, HEIGHT / 2 + 30, 3, 1.0, 1.0, Gosu::Color::WHITE)
end
end
def button_down(id)
case id
when Gosu::KbReturn
@drones.each(&:explode) unless @paused
when Gosu::KbSpace
reset_game if @game_over
when Gosu::KbEscape
@paused = !@paused unless @game_over
end
end
private
def handle_collisions
@drones.each do |drone|
# Check collision with operator
if drone.collides_with?(@operator) && !drone.exploded?
drone.explode
@game_over = true
next
end
# Check collision with zombies
@zombies.each do |zombie|
if drone.collides_with?(zombie) && !drone.exploded?
zombie.hit
drone.explode
@points += 1
end
end
end
end
def handle_explosions
@drones.each do |drone|
next unless drone.exploded?
@zombies.reject! do |zombie|
drone.collides_with?(zombie)
end
# Check collision with operator after explosion
if drone.collides_with?(@operator)
@game_over = true
end
end
end
def handle_zombie_movement
@zombies.each do |zombie|
zombie.move_towards(@operator.x, @operator.y)
end
end
def spawn_zombie
@zombies << Zombie.new
end
def spawn_drone
@drones << Drone.new(@operator.x, @operator.y - 100)
end
def check_game_over
@zombies.each do |zombie|
if Gosu.distance(zombie.x, zombie.y, @operator.x, @operator.y) < 30
@game_over = true
end
end
end
def reset_game
@operator = Operator.new
@drones = []
@zombies = []
@points = 0
@game_over = false
@paused = false
spawn_drone
end
end
class Operator
attr_reader :x, :y, :size
def initialize
@x = DroneGame::WIDTH / 2
@y = DroneGame::HEIGHT - 50
@image = Gosu::Image.new("operator.png")
@size = 100
end
def draw
@image.draw(@x - @size / 2, @y - @size / 2, 1)
end
end
class Drone
attr_reader :x, :y, :exploded, :size
def initialize(x, y)
@x, @y = x, y
@image = Gosu::Image.new("drone.png")
@explosion_image = Gosu::Image.new("explosion.png")
@exploded = false
@size = 100
@angle = 0 # Initialize angle
@explosion_timer = 0
end
def update
return if @exploded
@y -= 5 if Gosu.button_down?(Gosu::KbUp)
@y += 5 if Gosu.button_down?(Gosu::KbDown)
@x -= 5 if Gosu.button_down?(Gosu::KbLeft)
@x += 5 if Gosu.button_down?(Gosu::KbRight)
# Rotation
@angle -= 5 if Gosu.button_down?(Gosu::KbLeft)
@angle += 5 if Gosu.button_down?(Gosu::KbRight)
# Boundary checking
@x = [[@x, 0].max, DroneGame::WIDTH].min
@y = [[@y, 0].max, DroneGame::HEIGHT].min
if @explosion_timer > 0
@explosion_timer -= 1
end
end
def draw
if @exploded
if @explosion_timer > 0
@explosion_image.draw_rot(@x, @y, 1, @angle, 0.5, 0.5)
end
else
@image.draw_rot(@x, @y, 1, @angle)
end
end
def explode
@exploded = true
@explosion_timer = 550
end
def exploded?
@exploded
end
def collides_with?(object)
Gosu.distance(@x, @y, object.x, object.y) < (@size / 2 + object.size / 2)
end
end
class Zombie
attr_reader :x, :y, :size
def initialize
@x = rand * DroneGame::WIDTH
@y = 0
@image = Gosu::Image.new("zombie.png")
@size = 100
@speed = rand(0.05..0.2)
@alive = true
end
def update
return unless @alive
@y += @speed
end
def draw
@image.draw(@x - @size / 2, @y - @size / 2, 1)
end
def move_towards(target_x, target_y)
if @y < target_y
@y += @speed
elsif @y > target_y
@y -= @speed
end
if @x < target_x
@x += @speed
elsif @x > target_x
@x -= @speed
end
end
def hit
die if Gosu.distance(@x, @y, DroneGame::WIDTH / 2, DroneGame::HEIGHT - 50) < 50
end
def die
@alive = false
@speed = 0
end
def dead?
!@alive
end
end
DroneGame.new.show
Картинки додайте самі (не хочу випадково порушити авторське право):
drone.png
explosion.png
operator.png
zombie.png
Розмір для кожної з них має бути 100х100px (png з прозорістю).
Клас DroneGame
Головний клас гри, унаслідований від Gosu::Window.
Ініціалізує вікно гри, встановлює колір фону та шрифт для текстових елементів.
Містить логіку для оновлення гри (update) та відображення (draw), обробляє події натискання кнопок (button_down).
Клас Operator
Представляє оператора дрону.
Відповідає за його положення на екрані та відображення зображення оператора.
Клас Drone
Представляє дрон, який використовується для атаки.
Має можливість рухатися вліво, вправо, вгору та вниз, обробляє вибух (explode) та перевіряти зіткнення з іншими об'єктами.
Клас Zombie
Представляє зомбі, що нападають на оператора.
Рухається у напрямку оператора, може бути враженим дронами, що призводить до знищення.
Основні функції
Старт і перезавантаження гри: гра починається зі спавну оператора та дронів, які захищають його. Після поразки (коли зомбі досягають оператора), гравець може перезавантажити гру, натиснувши пробіл (Space).
Динамічні об'єкти: дрони та зомбі мають власні об'єкти, які оновлюють їх рух та стан.
Зіткнення і вибухи: логіка обробки зіткнень між дронами, зомбі та оператором, а також вибухів дронів, що призводять до знищення зомбі та кінця гри.
Наступним кроком я хочу додати звуки, промальовувати могилки для зомбі після їх знищення, покращити рух / управління дроном (зараз він крутиться навколо осі), зробити анімацію пропелерів дрона тощо. Я вирішив публікувати демки крок за кроком, бо написання всіх бажаних штук потребує дуже багато часу.
Наприклад, перед публікацією цього допису я додав кілька речей:
оператор дрона може сам себе випадково знищити дроном
додав границі до полю бою (до цього дрон міг улетіти за край вікна та умовно загубитись)
Цей блок містить всю необхідну інформацію для правильного цитування, включаючи дату публікації, назву допису, URL та інше.
Зверніть увагу, що посилатись потрібно на автора/авторку контенту (профіль, окремий допис тощо), а сайт цейво (tseivo.com) - лише майданчик для розміщення публікацій.
💡 Архівування сторінки у веб-архіві, такому як Wayback Machine, є корисною практикою, коли ви посилаєтесь на неї. Це дозволить зберегти стан сторінки на цей час і знизить ризики від зміни контенту або зміни структури сайту у майбутньому.
Автор/авторка пише під псевдонимом і не оприлюднює своє справжнє ім'я. Завжди перевіряйте достовірність контенту використовуючи декілька джерел (навіть, якщо контент опублікован під справжнім ім'ям).
Про зображення:
На нашій платформі ми приділяємо особливу увагу правильному вказуванню джерел зображень, щоб забезпечити дотримання авторських прав і прав на використання контенту. Ми намагаємося мотивувати використовувати виключно власні зображення, ресурси, що відповідають принципам fair use, або безкоштовні зображення з ліцензіями на кшталт Unsplash, які дозволяють вільне використання без порушення прав інтелектуальної власності.
Однак, враховуючи масштаб нашої платформи та різноманітність контенту, ми не можемо повністю контролювати всі публікації користувачів. Тому ми рекомендуємо перевіряти права на використання зображень та дотримуватися відповідних ліцензій, щоб уникнути можливих порушень авторських прав.
Інформація на сайті tseivo.com є суб'єктивною та відображає особисті погляди та досвід авторів та авторок блогів.
Використовуйте цей ресурс як одне з декількох джерел інформації під час своїх досліджень та прийняття рішень. Завжди застосовуйте критичне мислення. Людина сама несе відповідальність за свої рішення та дії.