Neulich habe ich auf Twitter von folgenden Wohltätigkeitsprojekten erfahren.
Ich spiele nicht viel PC-Spiele, aber es enthält Materialien, und als ich es mir ansah, dachte ich, ich sollte es kaufen, wenn ich es gespendet habe ... Die Buchstaben ** DragonRuby GTK ** waren da.
Es scheint, dass Sie mit Ruby plattformübergreifende Spiele erstellen können. (RubyMotion, das auf einmal vorherrschte, gehört jetzt zu dieser Familie)
Das scheint interessant zu sein, also habe ich es sofort geknallt und am Wochenende ein wenig gespielt.
Unten finden Sie eine Aufzeichnung zum Ausprobieren der macOS-Version.
Hello World
Bewegen wir es zuerst.
$ unzip dragonruby-gtk-macos.zip 
$ cd dragonruby-macos/
$ ./dragonruby
# ./Gleiches gilt für Dragonruby Mygame
 
Es funktionierte.
mygame / app / main.rb läuft.
In meiner Umgebung stöhnt der CPU-Lüfter nicht, selbst wenn ich ihn eine Weile laufen lasse. (Der Körper wird heiß)
Ich habe in dem Verzeichnis gearbeitet, in dem ich die Zip-Datei zuvor entpackt habe, aber ich werde versuchen, an einem anderen Ort zu arbeiten. (Um das Git-Management zu vereinfachen)
$ mkdir -p hello-dragonruby-gtk/mygame/app
$ cd hello-dragonruby-gtk/
$ cp ~/Downloads/dragonruby-macos/dragonruby .
$ cp ~/Downloads/dragonruby-macos/font.ttf .
Ich habe das Gefühl, dass der Befehl "dragonruby" nicht von einem anderen Ort aus aufgerufen werden soll, indem der Pfad festgelegt wird, also habe ich ihn kopiert.
Ich brauchte auch font.ttf.
(Insgesamt ca. 5,9 MB)
Wenn Sie es ausführen, werden verschiedene Verzeichnisse und Dateien erstellt, also gitignore sie zusammen.
.gitignore
# DragonRuby
/dragonruby
/font.ttf
/logs
/tmp
/exceptions
console_history.txt
Nachdem wir fertig sind, schreiben wir den Quellcode, um das Quadrat zu zeichnen.
mygame/app/main.rb
def tick args
  args.outputs.solids << [args.grid.center_x - 32, args.grid.h * 0.1, 64, 64]
end
Ich werde das machen.
./dragonruby
 
Ich konnte zeichnen.
Wenn Sie es zu "args.outputs.solids" hinzufügen, wird die Figur gezeichnet.
Da die Informationen zur Bildschirmgröße usw. in args.grid enthalten sind, verwende ich das auch.
Es ist ein bisschen einzigartig, aber ich denke nicht, dass es besonders schwierig ist.
Die grundlegenden Spezifikationen scheinen wie folgt zu sein.
Offiziell habe ich das Gefühl, dass ich diese Notation forciere, aber um ehrlich zu sein, es tut weh, mich zum ersten Mal zu töten ...
Andere Schreibstile wurden ebenfalls richtig vorbereitet.
Wenn dies der Fall ist, scheint es in Ordnung zu sein, es zum ersten Mal zu lesen.
mygame/app/main.rb
  args.outputs.solids << { x: args.grid.center_x - 32, y: args.grid.h * 0.1, w: 64, h: 64 }
Es scheint in Ordnung zu sein, die folgende Klasse zu definieren.
primitive_marker gibt einen Typ wie: solid zurückIch benutze diese Methode jetzt.
mygame/app/primitives.rb
class Primitive
  def initialize(attributes)
    attr_keys.each { |key| send("#{key}=", attributes[key]) }
  end
  def primitive_marker
    self.class.name.downcase
  end
  def attr_keys
    self.class.class_variable_get(:@@attr_keys)
  end
  def serialize
    attr_keys.map { |key| [key, send(key)] }.to_h
  end
  def inspect
    serialize.to_s
  end
  def to_s
    serialize.to_s
  end
end
class Solid < Primitive
  @@attr_keys = %i[x y w h r g b a]
  attr_accessor(*@@attr_keys)
end
Wenn Sie nicht "serialize", "inspect" und "to_s" definieren, wird die Konsole mit der Warnung gefüllt, wenn ein Fehler auftritt, sodass sie definiert ist.
(Ich habe @@ attr_keys dafür ...)
Dann laden Sie diese von main.rb.
mygame/app/main.rb
require 'app/primitives.rb'
def tick args
  args.outputs.solids << Solid.new(x: args.grid.center_x - 32, y: args.grid.h * 0.1, w: 64, h: 64)
end
Als anderes Beispiel als "Fest" zeichnen wir mit "Linie" eine Kreuzlinie in der Mitte. Es wird gezeichnet, indem es zu "args.outputs.lines" hinzugefügt wird.
Ich habe "Outputs" und "Grid" in die Variablen eingefügt, weil es schwierig ist, jedes Mal "Args" zu drücken.
mygame/app/primitives.rb
class Line < Primitive
  @@attr_keys = %i[x y x2 y2 r g b a]
  attr_accessor(*@@attr_keys)
end
mygame/app/main.rb
require 'app/primitives.rb'
def tick args
  outputs, grid = args.outputs, args.grid
  outputs.solids << Solid.new(x: grid.center_x - 32, y: grid.h * 0.1, w: 64, h: 64)
  outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
  outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
end
 
Es wurde bestätigt, dass das Quadrat in der Mitte links und rechts gezeichnet werden konnte.
Bevor die Tick-Methode kompliziert wird, klassifizieren Sie das Spiel selbst.
mygame/app/main.rb
require 'app/primitives.rb'
class Game
  attr_accessor :state, :outputs, :grid
  def tick
    output
  end
  def output
    outputs.solids << Solid.new(x: grid.center_x - 32, y: grid.h * 0.1, w: 64, h: 64)
    outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
    outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
  end
  def serialize
    {}
  end
  def inspect
    serialize.to_s
  end
  def to_s
    serialize.to_s
  end
end
 
$game = Game.new
def tick args
  $game.state = args.state
  $game.outputs = args.outputs
  $game.grid = args.grid
  $game.tick
end
Es gibt einen Helfer namens "attr_gtk", aber ich verwende hier "attr_accessor", weil ich direkt auf "state" usw. zugreifen wollte.
Verwenden Sie die linke und rechte Cursortaste, um das Quadrat zu bewegen (Spieler).
Die Informationen, die Sie über Frames hinweg aufbewahren möchten, werden im Status gespeichert.
Die Tastatureingabe kann über "inputs.keyboard.key name" erkannt werden. (Es scheint, dass Sie die Presse abholen und mit inputs.keyboard.key_held.key name halten können, aber ich habe es nicht verwendet)
Die folgenden Änderungen wurden an main.rb vorgenommen.
mygame/app/main.rb
 require 'app/primitives.rb'
 
 class Game
-  attr_accessor :state, :outputs, :grid
+  attr_accessor :state, :outputs, :grid, :inputs
 
   def tick
+    set_defaults
+    handle_inputs
+    update_state
     output
   end
 
+  def set_defaults
+    state.player_x ||= grid.center_x - 32
+    state.player_dx ||= 0
+  end
+
+  def handle_inputs
+    if inputs.keyboard.right
+      state.player_dx = 5
+    elsif inputs.keyboard.left
+      state.player_dx = -5
+    else
+      state.player_dx = 0
+    end
+  end
+
+  def update_state
+    state.player_x += state.player_dx
+  end
+
   def output
-    outputs.solids << Solid.new(x: grid.center_x - 32, y: grid.h * 0.1, w: 64, h: 64)
+    outputs.solids << Solid.new(x: state.player_x, y: grid.h * 0.1, w: 64, h: 64)
 
     outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
     outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
@@ -26,12 +50,13 @@ class Game
     serialize.to_s
   end
 end
 $game = Game.new
 
 def tick args
   $game.state = args.state
   $game.outputs = args.outputs
   $game.grid = args.grid
+  $game.inputs = args.inputs
   $game.tick
 end
Jetzt können Sie das Viereck verschieben.

Fügen Sie einen grauen Festkörper hinzu, der sich von einer Seite zur anderen bewegt. Ich habe keine neuen Elemente verwendet.
mygame/app/main.rb
   def set_defaults
     state.player_x ||= grid.center_x - 32
     state.player_dx ||= 0
+
+    state.enemy_x ||= grid.center_x - 32
+    state.enemy_dx ||= 5
   end
 
   def update_state
     state.player_x += state.player_dx
+
+    state.enemy_x += state.enemy_dx
+    if state.enemy_x < 0 || state.enemy_x > grid.w - 64
+      state.enemy_dx *= -1
+    end
   end
 
   def output
     outputs.solids << Solid.new(x: state.player_x, y: grid.h * 0.1, w: 64, h: 64)
+ 
+    outputs.solids << Solid.new(x: state.enemy_x, y: grid.h * 0.7, w: 64, h: 64, r: 150, g: 150, b: 150)
     outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
     outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
   end

Sie können eine Entität mit state.new_entity hinzufügen.
Es ist eine Möglichkeit, der Entität Informationen zu geben und diese Informationen zu verwenden, um sie mit "Solid" usw. auf dem Bildschirm anzuzeigen.
(Ich denke, es kann ohne eine Entität gemacht werden, aber ich werde es verwenden, weil es vorbereitet ist.)
Ich benutze key_up, um zu verhindern, dass es erschossen wird, indem ich es gedrückt halte.
Wenn die Kugel den Rand des Bildschirms erreicht, wird sie durch Setzen des Flags "tot" gelöscht. (Wenn es eine "tote" Flagge gibt, wird sie "abgelehnt")
mygame/app/main.rb
   def set_defaults
     state.player_x ||= grid.center_x - 32
     state.player_dx ||= 0
 
     state.enemy_x ||= grid.center_x - 32
     state.enemy_dx ||= 5
+
+    state.bullets ||= []
   end
 
   def handle_inputs
     if inputs.keyboard.right
       state.player_dx = 5
     elsif inputs.keyboard.left
       state.player_dx = -5
     else
       state.player_dx = 0
     end
+
+    if inputs.keyboard.key_up.space
+      state.bullets << state.new_entity(:bullet) do |bullet|
+        bullet.y = player_rect[:y]
+        bullet.x = player_rect[:x] + 16
+        bullet.size = 32
+        bullet.dy = 10
+        bullet.solid = { x: bullet.x, y: bullet.y, w: bullet.size, h: bullet.size, r: 255, g: 100, b: 100 }
+      end
+    end
   end
   def update_state
     state.player_x += state.player_dx
 
     state.enemy_x += state.enemy_dx
     if state.enemy_x < 0 || state.enemy_x > grid.w - 64
       state.enemy_dx *= -1
     end
+
+    state.bullets.each do |bullet|
+      bullet.y += bullet.dy
+      bullet.solid[:y] = bullet.y
+
+      if bullet.y > grid.h
+        bullet.dead = true
+      end
+    end
+    state.bullets = state.bullets.reject(&:dead)
   end
   def output
-    outputs.solids << Solid.new(x: state.player_x, y: grid.h * 0.1, w: 64, h: 64)
+    outputs.solids << Solid.new(player_rect)
 
     outputs.solids << Solid.new(x: state.enemy_x, y: grid.h * 0.7, w: 64, h: 64, r: 150, g: 150, b: 150)
+ 
+    outputs.solids << state.bullets.map(&:solid)
     outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
     outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
   end
+
+  private
+
+  def player_rect
+    { x: state.player_x, y: grid.h * 0.1, w: 64, h: 64 }
+  end

Sie können jetzt Kugeln abschießen.
Sie können "intersect_rect" verwenden, um den Treffer zu bestimmen.
mygame/app/main.rb
   def update_state
     state.player_x += state.player_dx
 
     state.enemy_x += state.enemy_dx
     if state.enemy_x < 0 || state.enemy_x > grid.w - 64
       state.enemy_dx *= -1
     end
 
     state.bullets.each do |bullet|
       bullet.y += bullet.dy
       bullet.solid[:y] = bullet.y
 
       if bullet.y > grid.h
         bullet.dead = true
       end
+      if bullet.solid.intersect_rect?(enemy_rect)
+        bullet.dead = true
+      end
     end
     state.bullets = state.bullets.reject(&:dead)
   end
 
   def output
     outputs.solids << Solid.new(player_rect)
 
-    outputs.solids << Solid.new(x: state.enemy_x, y: grid.h * 0.7, w: 64, h: 64, r: 150, g: 150, b: 150)
+    outputs.solids << Solid.new(enemy_rect.merge(r: 150, g: 150, b: 150))
 
     outputs.solids << state.bullets.map(&:solid)
 
     outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
     outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
   end
+
+  def enemy_rect
+    { x: state.enemy_x, y: grid.h * 0.7, w: 64, h: 64 }
+  end

Es ist ein wenig verwirrend, aber wenn es einen Feind trifft, verschwindet die Kugel.
Wenn die Kugel den Feind trifft, sind es +2 Punkte, wenn sie nicht trifft, sind es -1 Punkte und die Summe wird oben rechts angezeigt.
Verwenden Sie Label, um den Text anzuzeigen.
Ich habe Drücken Sie Start 2P - Google Fonts verwendet, um eine punktförmige Schriftart zu erstellen. (Fügen Sie das heruntergeladene und erweiterte in "mygame / fonts" ein
mygame/app/primitives.rb
class Label < Primitive
  @@attr_keys = %i[x y text size_enum alignment_enum font r g b a]
  attr_accessor(*@@attr_keys)
end
mygame/app/main.rb
   def set_defaults
     state.player_x ||= grid.center_x - 32
     state.player_dx ||= 0
 
     state.enemy_x ||= grid.center_x - 32
     state.enemy_dx ||= 5
 
     state.bullets ||= []
+
+    state.score ||= 0
   end
  
   def update_state
     state.player_x += state.player_dx
 
     state.enemy_x += state.enemy_dx
     if state.enemy_x < 0 || state.enemy_x > grid.w - 64
       state.enemy_dx *= -1
     end
 
     state.bullets.each do |bullet|
       bullet.y += bullet.dy
       bullet.solid[:y] = bullet.y
 
       if bullet.y > grid.h
         bullet.dead = true
+        state.score -= 1
       end
       if bullet.solid.intersect_rect?(enemy_rect)
         bullet.dead = true
+        state.score += 2
       end
     end
     state.bullets = state.bullets.reject(&:dead)
   end
 
   def output
     outputs.solids << Solid.new(player_rect)
 
     outputs.solids << Solid.new(enemy_rect.merge(r: 150, g: 150, b: 150))
 
     outputs.solids << state.bullets.map(&:solid)
+
+    outputs.labels << Label.new(
+      x: grid.w * 0.99,
+      y: grid.h * 0.98,
+      text: state.score,
+      alignment_enum: 2,
+      font: 'fonts/Press_Start_2P/PressStart2P-Regular.ttf'
+    )
 
     outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
     outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)
   end

Jetzt, wo ich eine Punktzahl habe, kann ich (vorerst) spielen.
Ich bin einsam, wenn es quadratisch bleibt, also werde ich ein Bild verwenden. (Das Bild ist das, das ich zuvor gezeichnet habe, also ist die Qualität, dass ...)
Platziere player.png , feind.png und bull.png in mygame / sprites.
Ändern Sie danach den Code, um "Sprite" anstelle von "Solid" zu verwenden. Ich brauche die Querlinie nicht mehr, also werde ich sie löschen.
mygame/app/primitives.rb
class Sprite < Primitive
  @@attr_keys = %i[
    x y w h path angle a r g b
    source_x source_y source_w source_h
    flip_horizontally flip_vertically
    angle_anchor_x angle_anchor_y
    tile_x tile_y tile_w tile_h
  ]
  attr_accessor(*@@attr_keys)
end
mygame/app/main.rb
   def handle_inputs
     if inputs.keyboard.right
       state.player_dx = 5
     elsif inputs.keyboard.left
       state.player_dx = -5
     else
       state.player_dx = 0
     end
 
     if inputs.keyboard.key_up.space
       state.bullets << state.new_entity(:bullet) do |bullet|
         bullet.y = player_rect[:y]
         bullet.x = player_rect[:x] + 16
         bullet.size = 32
         bullet.dy = 10
-        bullet.solid = { x: bullet.x, y: bullet.y, w: bullet.size, h: bullet.size, r: 255, g: 100, b: 100 }
+        bullet.sprite = { x: bullet.x, y: bullet.y, w: bullet.size, h: bullet.size, r: 255, g: 100, b: 100, path: 'sprites/bullet.png' }
       end
     end
   end
 
   def update_state
     state.player_x += state.player_dx
 
     state.enemy_x += state.enemy_dx
     if state.enemy_x < 0 || state.enemy_x > grid.w - 64
       state.enemy_dx *= -1
     end
 
     state.bullets.each do |bullet|
       bullet.y += bullet.dy
-      bullet.solid[:y] = bullet.y
+      bullet.sprite[:y] = bullet.y
 
       if bullet.y > grid.h
         bullet.dead = true
         state.score -= 1
       end
-      if bullet.solid.intersect_rect?(enemy_rect)
+      if bullet.sprite.intersect_rect?(enemy_rect)
         bullet.dead = true
         state.score += 2
       end
     end
     state.bullets = state.bullets.reject(&:dead)
   end
 
   def output
-    outputs.solids << Solid.new(player_rect)
+    outputs.sprites << Sprite.new(player_rect.merge(path: 'sprites/player.png'))
 
-    outputs.solids << Solid.new(enemy_rect.merge(r: 150, g: 150, b: 150))
+    outputs.sprites << Sprite.new(enemy_rect.merge(r: 150, g: 150, b: 150, path: 'sprites/enemy.png'))
 
-    outputs.solids << state.bullets.map(&:solid)
+    outputs.sprites << state.bullets.map(&:sprite)
 
     outputs.labels << Label.new(
       x: grid.w * 0.99,
       y: grid.h * 0.98,
       text: state.score,
       alignment_enum: 2,
       font: 'fonts/Press_Start_2P/PressStart2P-Regular.ttf'
     )
-    outputs.lines << Line.new(x: 0, y: grid.center_y, x2: grid.w, y2: grid.center_y)
-    outputs.lines << Line.new(x: grid.center_x, y: 0, x2: grid.center_x, y2: grid.h)     
   end

Im Vergleich zum Anfang sieht es sehr ähnlich aus!
Bereiten Sie die Metadaten vor.
Kopieren Sie als Symbol player.png.
mygame/metadata/game_metadata.txt
devid=hello-dragonruby-gtk
devtitle=Hello DragonRuby GTK
gameid=hello-dragonruby-gtk
gametitle=Hello DragonRuby GTK
version=0.1
icon=metadata/icon.png
Ich habe Dragonruby-Publish kopiert, aber es hat aus irgendeinem Grund nicht funktioniert Tun Sie dies in dem Verzeichnis, in das Sie DragonRuby heruntergeladen und entpackt haben. (Informationen zum Kopieren und Verschieben finden Sie im folgenden Postskriptum.)
$ cp mygame/sprites/player.png mygame/metadata/icon.png
$ cp -r mygame/ ~/Downloads/dragonruby-macos/hello-dragonruby-gtk
$ cd ~/Downloads/dragonruby-macos
$ ./dragonruby-publish --only-package hello-dragonruby-gtk
Dadurch wird die HTML5-Version zusammen mit den Dateien für andere Plattformen ausgegeben.
(builds/hello-dragonruby-gtk-html5-0.1)
Wenn Sie dies veröffentlichen, können Sie es in Ihrem Browser abspielen. Da es eine große Sache ist, habe ich es auf GitHub Pages gestellt.
https://tnantoka.github.io/hello-dragonruby-gtk/
Es hat funktioniert, als ich verschiedene andere Dinge als "Dragonruby-Publish" kopiert habe.
$ cp ~/Downloads/dragonruby-macos/dragonruby-publish .
$ cp ~/Downloads/dragonruby-macos/.dragonruby .
$ cp ~/Downloads/dragonruby-macos/*.png .
$ cp ~/Downloads/dragonruby-macos/open-source-licenses.txt .
$ ./dragonruby-publish --only-package
```
 Es gibt noch mehr Dinge zu tun ".gitignore".
#### **`.gitignore`**
```python
# DragonRuby
/dragonruby*
/font.ttf
/logs
/tmp
/exceptions
/console_history.txt
/.dragonruby
/builds
/console-logo.png
/open-source-licenses.txt
```
# Quellcode
 Es wird unten veröffentlicht.
 (DragonRuby wird separat benötigt)
https://github.com/tnantoka/hello-dragonruby-gtk/
# Impressionen
 Ich konnte ohne große Sucht spielen.
 Kann es in der Produktion verwendet werden? Ich weiß nicht was es ist, aber
 Es schien brauchbar genug zu sein, um ein kleines Ding zu machen.
 Ich wollte mit Ruby kreativ codieren, was ich gewohnt bin zu schreiben. Kann ich es also als Werkzeug verwenden? Ich denke, dass.
 (Zu diesem Zweck möchte ich, dass Sie eine physische Engine installieren und das Zeichnen verbessern.)
 Ich werde versuchen, Zeit zu finden und sie erneut zu berühren.
# Verweise
 Es gibt keine API-Referenz (?), Also habe ich mich umgesehen, während ich mich umgesehen habe.
- README.md
 ――Lesen Sie zuerst hier
- CHEATSHEET.md
 ――Lesen Sie das auch
- [DragonRuby Game Toolkit by DragonRuby](https://dragonruby.itch.io/dragonruby-gtk)
 - Es gibt eine Beschreibung wie Entity, die nicht in README enthalten ist
- mygame/documantion
 - Solid, Sprite usw. erklären jedes Element so wie es ist
- samples
 - Es gibt 67 Muster (MIT-Lizenz!)
Recommended Posts