L'autre jour, j'ai découvert les projets caritatifs suivants sur Twitter.
[La campagne pour jouer à 1000 jeux indépendants avec un don d'au moins 5 $ a commencé. Dans le cadre du mouvement anti-discrimination raciale --Netorabo](https://nlab.itmedia.co.jp/nl/articles/2006/10/news135 .html)
Je ne joue pas beaucoup aux jeux PC, mais il contient du matériel, et quand je le regardais, j'ai trouvé les mots ** DragonRuby GTK **.
Il semble que vous puissiez créer des jeux multiplateformes avec Ruby. (RubyMotion, qui était prédominante à la fois, fait maintenant partie de cette famille)
Cela semble intéressant, alors je l'ai immédiatement sauté et j'ai joué un peu le week-end.
Vous trouverez ci-dessous un enregistrement de l'essai de la version macOS.
Hello World
Commençons par le déplacer.
$ unzip dragonruby-gtk-macos.zip
$ cd dragonruby-macos/
$ ./dragonruby
# ./Idem pour dragonruby mygame
Ça a marché.
mygame / app / main.rb
est en cours d'exécution.
Dans mon environnement, le ventilateur du processeur ne gémit pas même si je le fais fonctionner pendant un moment. (Le corps devient chaud)
J'ai travaillé dans le répertoire où j'ai décompressé le zip plus tôt, mais j'essaierai de travailler dans un autre emplacement. (Pour faciliter la gestion de git)
$ mkdir -p hello-dragonruby-gtk/mygame/app
$ cd hello-dragonruby-gtk/
$ cp ~/Downloads/dragonruby-macos/dragonruby .
$ cp ~/Downloads/dragonruby-macos/font.ttf .
Je pense que la commande dragonruby
n'est pas censée être appelée à partir d'un autre emplacement en définissant le PATH, donc je l'ai copiée.
J'avais aussi besoin de font.ttf
.
(Environ 5,9 Mo au total)
Lorsque vous l'exécutez, divers répertoires et fichiers seront créés, donc ignorez-les ensemble.
.gitignore
# DragonRuby
/dragonruby
/font.ttf
/logs
/tmp
/exceptions
console_history.txt
Maintenant que nous sommes prêts, écrivons le code source pour dessiner le carré.
mygame/app/main.rb
def tick args
args.outputs.solids << [args.grid.center_x - 32, args.grid.h * 0.1, 64, 64]
end
Je le ferai.
./dragonruby
J'ai pu dessiner.
Ajoutez-le à ʻargs.outputs.solids pour dessiner la figure. De plus, puisque ʻargs.grid
contient des informations telles que la taille de l'écran, je l'utilise.
C'est un peu unique, mais je ne pense pas que ce soit particulièrement difficile.
Les spécifications de base semblent être les suivantes.
Officiellement, j'ai l'impression de pousser cette notation, mais pour être honnête, ça fait mal de me tuer pour la première fois ...
D'autres styles d'écriture ont également été préparés correctement.
Si tel est le cas, il semble normal de le lire pour la première fois.
mygame/app/main.rb
args.outputs.solids << { x: args.grid.center_x - 32, y: args.grid.h * 0.1, w: 64, h: 64 }
Il semble correct de définir la classe suivante.
primitive_marker
renvoie un type tel que: solid
--A les mêmes attributs que la clé de notation de hachageJ'utilise cette méthode pour le moment.
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
Si vous ne définissez pas serialize
, ʻinspect,
to_s, la console sera remplie avec l'avertissement lorsqu'une erreur se produit, elle est donc définie. (J'ai
@@ attr_keys` pour ça ...)
Ensuite, chargez-le depuis 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
À titre d'exemple autre que "Solide", dessinons une ligne transversale au milieu en utilisant "Ligne". Il est dessiné en l'ajoutant à ʻargs.outputs.lines`.
J'ai mis ʻoutputs et
grid dans les variables car il est difficile de frapper ʻargs
à chaque fois.
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
Il a été confirmé que le carré pouvait être dessiné au centre de la gauche et de la droite.
Avant que la méthode tick
ne devienne compliquée, classez le jeu lui-même.
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
Il y a un assistant appelé ʻattr_gtk, mais j'utilise ʻattr_accessor
ici parce que je voulais accéder directement à state
etc.
Déplacez le carré (player
) avec les touches fléchées gauche et droite.
Les informations que vous souhaitez conserver dans les cadres sont stockées dans state
.
L'entrée au clavier peut être détectée par ʻinputs.keyboard.key name. (Il semble que vous puissiez récupérer la cale par ʻinputs.keyboard.key_held.key name
, mais je ne l'utilise pas)
Les modifications suivantes ont été apportées à main.rb
.
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
Vous pouvez maintenant déplacer le quadrilatère.
Ajoutez un "solide" gris qui se déplace d'un côté à l'autre. Je n'ai utilisé aucun nouvel élément.
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
Vous pouvez ajouter une entité avec state.new_entity
.
Vous pouvez donner des informations à l'entité et utiliser ces informations pour l'afficher à l'écran avec Solid
etc.
(Je pense que cela peut être fait sans entité, mais je vais l'utiliser parce qu'il est préparé.)
J'utilise key_up
pour l'empêcher d'être abattu en le maintenant enfoncé.
De plus, lorsque la balle atteint le bord de l'écran, elle est supprimée en définissant le drapeau «mort». (S'il y a un drapeau «mort», ce sera «rejet»)
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
Vous pouvez maintenant tirer des balles.
Vous pouvez utiliser ʻintersect_rect` pour le jugement de hit.
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
C'est un peu déroutant, mais quand il frappe un ennemi, la balle disparaît.
Si la balle touche l'ennemi, ce sera +2 points, si elle ne touche pas, ce sera -1 point, et le total sera affiché en haut à droite.
Utilisez Label
pour afficher le texte.
J'ai utilisé Press Start 2P --Google Fonts pour le faire ressembler à un point. (Mettez celui téléchargé et développé dans mygame / fonts
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
Maintenant que j'ai un score, je peux jouer (pour le moment).
Je suis seul si ça reste carré, alors je vais utiliser une image. (L'image est celle que j'ai dessinée avant, donc la qualité est que ...)
Placez player.png
, ʻenemy.png et
bullet.png dans
mygame / sprites`.
Après cela, changez le code pour utiliser Sprite
au lieu de Solide
.
Je n'ai plus besoin de la ligne croisée, alors je vais l'effacer.
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
Par rapport au début, ça ressemble beaucoup à ça!
Préparez les métadonnées.
Pour l'icône, copiez 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
J'ai copié dragonruby-publish` mais cela n'a pas fonctionné pour une raison quelconque, donc Faites-le dans le répertoire où vous avez téléchargé et décompressé DragonRuby. (Reportez-vous au post-scriptum ci-dessous pour savoir comment copier et déplacer)
$ 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
Cela affichera la version HTML5 avec les fichiers pour d'autres plates-formes.
(builds/hello-dragonruby-gtk-html5-0.1
)
Si vous publiez ceci, vous pouvez le lire dans votre navigateur. Comme c'est un gros problème, je l'ai mis sur les pages GitHub.
https://tnantoka.github.io/hello-dragonruby-gtk/
Cela fonctionnait quand je copiais diverses choses autres que dragonruby-publish
.
$ 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
```
Il y a plus de choses à faire `.gitignore`.
#### **`.gitignore`**
```python
# DragonRuby
/dragonruby*
/font.ttf
/logs
/tmp
/exceptions
/console_history.txt
/.dragonruby
/builds
/console-logo.png
/open-source-licenses.txt
```
# Code source
Il est publié ci-dessous.
(DragonRuby est requis séparément)
https://github.com/tnantoka/hello-dragonruby-gtk/
# Impressions
J'ai pu jouer sans grande dépendance.
Peut-il être utilisé en production? Je ne sais pas ce que c'est, mais
Cela semblait assez utilisable pour faire un petit truc.
Je voulais faire du codage créatif avec Ruby, que j'ai l'habitude d'écrire, puis-je l'utiliser comme un outil? Je pense que.
(Pour cela, j'aimerais que vous installiez un moteur physique et amélioriez le dessin.)
J'essaierai de trouver le temps et de le toucher à nouveau.
# Les références
Il n'y a pas de référence API (?), J'ai donc cherché tout en regardant autour de moi.
- README.md
――Lisez d'abord ici
- CHEATSHEET.md
――Lisez ceci aussi
- [DragonRuby Game Toolkit by DragonRuby](https://dragonruby.itch.io/dragonruby-gtk)
--Il existe une description telle que Entity qui n'est pas dans README
- mygame/documantion
--Solid, Sprite, etc. expliquera chaque élément tel quel
- samples
--Il y a 67 échantillons (licence MIT!)
Recommended Posts