Cała oryginalna treść jest tworzona po ukraińsku. Nie wszystkie treści zostały jeszcze przetłumaczone. Niektóre posty mogą być dostępne tylko po ukraińsku.Dowiedz się więcej

Pisujemy demo-grę Drones vs Zombies (Gosu / Ruby)

Okładka posta: Pisujemy demo-grę Drones vs Zombies (Gosu / Ruby)
Spis treściKliknij link, aby przejść do wybranego miejsca
Ta treść została automatycznie przetłumaczona z ukraińskiego.
```html
Już napisałem minimalny przegląd funkcjonalności biblioteki gosu. Aby napisać prostą grę 2D, tak naprawdę nie potrzeba dużo kodu. Można również uniknąć elementów graficznych i używać prostych figur (narysowanych programowo). Na początek (podczas nauki silnika) tak właśnie trzeba robić, ale skaczący prostokąt może nie być tak fajny, jak coś narysowanego.
Wziąłem darmowe ikony (atrybucja obowiązkowa zgodnie z warunkami użytkowania) Freepik na stronie Flaticon. Grafikę można znaleźć całkowicie za darmo, narysować samodzielnie, zamówić lub wygenerować za pomocą SI. Generowana treść może być odpowiednia do procesu tworzenia, ale do wydania na świat lepiej skorzystać z usług artystów i wspierać ich ekonomię oraz twórczość.
No to wróćmy do tematu tego wpisu - demo gry Drones vs Zombies. Koncepcja jest dość prosta - za pomocą drona trzeba zniszczyć zombie, które próbują dotrzeć do pilota. Demo nie jest idealne, ale dobrze demonstruje, że około 250 linii kodu i kilka obrazków mogą przekształcić pomysł/koncepcję gry w demo.
Sam kod mojego demo:
# Drones VS Zombies v.1.0.0

# Ikony: https://www.flaticon.com/authors/freepik
# Kod:  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("Punkty: #{@points}", 10, 10, 2)
    if @game_over
      @font.draw_text("Koniec gry! Ostateczny wynik: #{@points}", WIDTH / 2 - 100, HEIGHT / 2, 3, 1.0, 1.0, Gosu::Color::RED)
      @font.draw_text("Naciśnij Spację, aby zrestartować", WIDTH / 2 - 100, HEIGHT / 2 + 30, 3, 1.0, 1.0, Gosu::Color::WHITE)
    elsif @paused
      @font.draw_text("Wstrzymane", WIDTH / 2 - 50, HEIGHT / 2, 3, 1.0, 1.0, Gosu::Color::YELLOW)
      @font.draw_text("Naciśnij Esc, aby wznowić", 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|
      # Sprawdź kolizję z operatorem
      if drone.collides_with?(@operator) && !drone.exploded?
        drone.explode
        @game_over = true
        next
      end

      # Sprawdź kolizję z zombie
      @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

      # Sprawdź kolizję z operatorem po eksplozji
      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  # Zainicjalizuj kąt
    @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)

    # Rotacja
    @angle -= 5 if Gosu.button_down?(Gosu::KbLeft)
    @angle += 5 if Gosu.button_down?(Gosu::KbRight)

    # Sprawdzanie granic
    @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
Obrazki dodajcie sami (nie chcę przypadkowo naruszyć praw autorskich):
  • drone.png
  • explosion.png
  • operator.png
  • zombie.png
Rozmiar dla każdego z nich powinien wynosić 100x100px (png z przezroczystością).

Klasa DroneGame

  • Główny klasa gry, dziedzicząca po Gosu::Window.
  • Inicjalizuje okno gry, ustawia kolor tła i czcionkę dla elementów tekstowych.
  • Zawiera logikę do aktualizacji gry (update) i wyświetlania (draw), obsługuje zdarzenia naciśnięcia przycisków (button_down).

Klasa Operator

  • Reprezentuje operatora drona.
  • Odpowiada za jego położenie na ekranie i wyświetlanie obrazu operatora.

Klasa Drone

  • Reprezentuje drona, który jest używany do ataku.
  • Ma możliwość poruszania się w lewo, w prawo, w górę i w dół, obsługuje eksplozję (explode) oraz sprawdza kolizje z innymi obiektami.

Klasa Zombie

  • Reprezentuje zombie, które atakują operatora.
  • Porusza się w kierunku operatora, może być trafione przez drony, co prowadzi do zniszczenia.

Główne funkcje

  • Start i restart gry: gra zaczyna się od pojawienia się operatora i dronów, które go chronią. Po porażce (gdy zombie dotrą do operatora), gracz może zrestartować grę, naciskając spację (Space).
  • Dynamiczne obiekty: drony i zombie mają własne obiekty, które aktualizują ich ruch i stan.
  • Kolizje i eksplozje: logika obsługi kolizji między dronami, zombie i operatorem, a także eksplozji dronów, które prowadzą do zniszczenia zombie i końca gry.
Następnym krokiem chcę dodać dźwięki, narysować groby dla zombie po ich zniszczeniu, poprawić ruch / sterowanie dronem (teraz kręci się wokół osi), zrobić animację śmigieł drona itd. Postanowiłem publikować demo krok po kroku, ponieważ napisanie wszystkich pożądanych rzeczy zajmuje bardzo dużo czasu.
Дрон вбив оператора :(
Дрон вбив оператора :(
Na przykład, przed publikacją tego wpisu dodałem kilka rzeczy:
  • operator drona może przypadkowo zniszczyć się dronem
  • dodałem granice do pola bitwy (wcześniej dron mógł wylecieć za krawędź okna i w zasadzie zniknąć)
```

Ten post nie ma jeszcze żadnych dodatków od autora.

[Ruby] Czym różnią się zmienne zaczynające się od @, @@ i $?
23 cze 14:00

[Ruby] Czym różnią się zmienne zaczynające się od @, @@ i $?

meme code
meme code@memecode
Co to jest funkcja w programowaniu?
24 cze 18:15

Co to jest funkcja w programowaniu?

meme code
meme code@memecode
[Fix] extconf.rb nie powiódł się podczas instalacji biblioteki Ruby Gosu
27 cze 16:38

[Fix] extconf.rb nie powiódł się podczas instalacji biblioteki Ruby Gosu

meme code
meme code@memecode
Jak zrobić pusty commit w gicie?
28 cze 08:33

Jak zrobić pusty commit w gicie?

meme code
meme code@memecode
Biblioteka Ruby Gosu do tworzenia gier 2D
29 cze 08:48

Biblioteka Ruby Gosu do tworzenia gier 2D

meme code
meme code@memecode
Gosu Ruby Tutorial - пройдемось по офіційній документації
3 lip 11:50

Gosu Ruby Tutorial - пройдемось по офіційній документації

meme code
meme code@memecode
Jak naprawić awarię Windows spowodowaną przez CrowdStrike?
19 lip 13:53

Jak naprawić awarię Windows spowodowaną przez CrowdStrike?

meme code
meme code@memecode
Co oznacza .map(&:name) w Ruby?
28 lip 11:18

Co oznacza .map(&:name) w Ruby?

meme code
meme code@memecode
Jak działa metoda map w Ruby? Przegląd działania metody z przykładami
30 lip 07:33

Jak działa metoda map w Ruby? Przegląd działania metody z przykładami

meme code
meme code@memecode
Co oznacza kropka na początku pliku (.gitignore, .DS_Store, .bashrc itd.)?
2 sie 13:15

Co oznacza kropka na początku pliku (.gitignore, .DS_Store, .bashrc itd.)?

meme code
meme code@memecode
Co to jest .gitignore? Do czego jest potrzebny i jak go używać
2 sie 14:58

Co to jest .gitignore? Do czego jest potrzebny i jak go używać

meme code
meme code@memecode
Jak usunąć plik .DS_Store z repozytorium Git?
2 sie 19:34

Jak usunąć plik .DS_Store z repozytorium Git?

meme code
meme code@memecode