Sketchup es un software de modelado 3D muy fácil de usar, y permite dsearrollo secundario mediante Ruby.
Hoy he creado un pequeño juego de Snake dentro de Sketchup. A continuación, los pasos seguidos.
Demostración
Las reglas del juego son las siguientes:
- El jugador controla la serpiente con las teclas de dirección.
- Al comer la comida, la serpiente crece y la putnuación aumenta.
- Si la cabeza choca contra el borde del mapa o contra su propio cuerpo, el juego termina.
Archivo principal
# Punto de entrada
require 'json'
require_relative 'ayuda.rb'
module SerpienteIngenua
$juego = nil # Variable para instancia de la serpiente
class Indice
@@dialogo = nil
@@dialogoConfig = nil
@@dialogoRanking = nil
def initialize
cmd = UI::Command.new("SerpienteIngenua") {
@@dialogo = UI::WebDialog.new("SerpienteIngenua", false, "", 229, 172, 5, 90, false)
@@dialogo.set_file(__dir__ + '/index.html')
@@dialogoConfig = UI::WebDialog.new("SerpienteIngenua_config", false, "", 262, 315, 235, 90, false)
@@dialogoConfig.set_file(__dir__ + '/set.html')
@@dialogoRanking = UI::WebDialog.new("SerpienteIngenua_ranking", false, "", 290, 320, 5, 260, false)
@@dialogoRanking.set_url('http://00a00.com/snake/')
@@dialogo.show unless @@dialogo.visible?
@@dialogo.add_action_callback('iniciar') do
if Sketchup.active_model.active_entities.length > 0
respuesta = UI.messagebox("Hay un modelo existente, ¿desea limpiarlo?\n(Nota: debe limpiarse para jugar)", MB_YESNO)
if respuesta == 6
Sketchup.active_model.active_entities.clear!
load __dir__ + '/Base.rb'
require_relative 'Camara.rb'
require_relative 'Mapa.rb'
load __dir__ + '/Comida.rb'
load __dir__ + '/Serpiente.rb'
load __dir__ + '/HerramientaSerpiente.rb'
Camara.new # Inicializar cámara
Mapa.new.mostrar # Mostrar escenario
Comida.new.mostrar # Mostrar comida
$juego = Serpiente.new # Instancia de la serpiente
$juego.iniciar # Iniciar juego
Sketchup.active_model.select_tool(HerramientaSerpiente.new)
@@dialogo.close if @@dialogo.visible?
@@dialogoConfig.close if @@dialogo.visible?
@@dialogoRanking.close if @@dialogo.visible?
end
end
end
@@dialogo.add_action_callback('listo') do
# Generar nombre aleatorio
longitud = 8
caracteres = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
nombre = ""
1.upto(longitud) { |i| nombre << caracteres[rand(caracteres.size - 1)] }
config('apodo', nombre)
end
@@dialogo.add_action_callback('configurar') { @@dialogoConfig.show }
@@dialogo.add_action_callback('ranking') { @@dialogoRanking.show }
@@dialogoConfig.add_action_callback('ok') do |dialogo, json|
hash = JSON.parse(json)
hash.each { |k, v| config(k, v) }
dialogo.close
end
@@dialogoConfig.add_action_callback('restaurar') do |dialogo, json|
json = configRec()
@@dialogoConfig.execute_script("fill(#{json})")
end
@@dialogoConfig.add_action_callback('inicio') do |dialogo, json|
json = configAll()
@@dialogoConfig.execute_script("data = #{json}")
end
}
cmd.small_icon = cmd.large_icon = __dir__ + "/assets/icon.png"
cmd.menu_text = "SerpienteIngenua"
barra = UI::Toolbar.new("SerpienteIngenua")
barra = barra.add_item(cmd)
barra.show
menu = UI.menu('Plugins').add_submenu('SerpienteIngenua (Snake)')
menu.add_item
end
end
end
SerpienteIngenua::Indice.new
Clase base
# Clase base
module SerpienteIngenua
class Base
@@modelo = Sketchup.active_model # Modelo
@@vista = @@modelo.active_view # Vista
@@entidades = @@modelo.active_entities # Entidades
@@materiales = @@modelo.materials # Materiales
@@anchoMapa = config('anchoMapa').to_i # Ancho del mapa
@@altoMapa = config('altoMapa').to_i # Alto del mapa
@@altoSerpiente = config('altoSerpiente').to_i # Altura de la serpiente
@@anchoSerpiente = config('anchoSerpiente').to_i # Ancho de la serpiente
@@numSegmentos = config('numSegmentos').to_i # Número de segmentos
@@nivel = config('nivel') # Nivel de dificultad
@@direccion = ARRIBA # Dirección inicial
@@serpienteActual = [] # Cuerpo actual de la serpiente
@@serpienteArr = [] # Cuerpo inicial de la serpiente
@@comida = [] # Almacena la comida
@@listaNegra = [] # Lista negra de posiciones de comida
@@idTemporizador = nil # ID del temporizador
@@puntuacion = 0 # Puntuación
end
end
Clase de cámara
# Cámara
module SerpienteIngenua
class Camara < Base
def initialize
iniciar
end
def iniciar
ojo = [0, 0, 1300]; objetivo = [0, 0, 0]; arriba = [0, 1, 0]
camara_inicio = Sketchup::Camera.new(ojo, objetivo, arriba)
@@vista.camera = camara_inicio
end
end
end
Clase del mapa
# Mapa
module SerpienteIngenua
class Mapa < Base
def mostrar
cesped # Crear césped
end
def cesped
archivo = __dir__ + '/assets/cesped.skm'
material = @@materiales.load(archivo)
pt1 = Geom::Point3d.new(-@@anchoMapa/2, -@@altoMapa/2, 0)
pt2 = Geom::Point3d.new(@@anchoMapa/2, -@@altoMapa/2, 0)
pt3 = Geom::Point3d.new(@@anchoMapa/2, @@altoMapa/2, 0)
pt4 = Geom::Point3d.new(-@@anchoMapa/2, @@altoMapa/2, 0)
grupo = @@entidades.add_group
grupo.entities.add_face(pt1, pt2, pt3, pt4)
grupo.material = material
end
end
end
Clase de la serpiente
# Serpiente
module SerpienteIngenua
class Serpiente < Base
def initialize
@@serpienteArr = inicializarSerpiente(@@numSegmentos)
@arriba = 38; @abajo = 40; @izquierda = 37; @derecha = 39
@direccion = @@direccion
@posX = rand(-@@anchoMapa..@@anchoMapa) / 2 / 10 * 10
@posY = rand(-@@altoMapa..@@altoMapa) / 2 / 10 * 10
end
def iniciar
crearSerpiente # Dibujar serpiente
velocidad = 0.5
case @@nivel
when 'Normal'; velocidad = 0.4
when 'Valiente'; velocidad = 0.1
when 'Infierno'; velocidad = 0.03
when 'Pesadilla'; velocidad = 0.006
end
tiempo = 1
@@idTemporizador = UI.start_timer(velocidad, true) do
mover(@direccion)
tiempo += 1
end
end
def establecer(direccion)
@direccion = direccion
end
# Dibujar serpiente
def crearSerpiente
@@serpienteActual.clear # Limpiar estado actual
@@serpienteArr.each do |cuadro|
xBase = @@anchoSerpiente / 2
yBase = @@anchoSerpiente / 2
punto1 = Geom::Point3d.new(cuadro[0]-xBase, cuadro[1]-yBase, 0)
punto2 = Geom::Point3d.new(cuadro[0]+xBase, cuadro[1]-yBase, 0)
punto3 = Geom::Point3d.new(cuadro[0]+xBase, cuadro[1]+yBase, 0)
punto4 = Geom::Point3d.new(cuadro[0]-xBase, cuadro[1]+yBase, 0)
grupoCuadro = @@entidades.add_group
cara = grupoCuadro.entities.add_face(punto1, punto2, punto3, punto4)
cara.reverse!.pushpull(@@altoSerpiente)
@@serpienteActual.push(grupoCuadro)
end
end
# Mover
def mover(direccion)
arregloTmp = [] # Contenedor temporal
cabeza = @@serpienteArr.last # Cabeza de la serpiente
# Verificar colisión con lista negra
@@listaNegra.each do |pos|
if cabeza == pos
finJuego
return
end
end
# Verificar colisión con bordes
if cabeza[0].abs >= @@anchoMapa/2 || cabeza[1].abs >= @@altoMapa/2
finJuego
return
end
# Verificar si come comida
if cabeza == @@comida
@@puntuacion += 10
@@listaNegra.push(@@comida)
@posX = rand(-(@@anchoMapa-50)..(@@anchoMapa-50)) / 2 / 10 * 10
@posY = rand(-@@altoMapa..@@altoMapa) / 2 / 10 * 10
@@serpienteArr.push([@posX+@@anchoSerpiente/2, @posY+@@altoSerpiente/2])
Comida.new.mostrar
end
# Desplazar el array
ultimoValor = @@serpienteArr.last.clone
(0...@@serpienteArr.size-1).each do |i|
arregloTmp[i] = @@serpienteArr[i+1]
end
arregloTmp.push(ultimoValor)
case direccion
when @arriba; arregloTmp.last[1] += @@anchoSerpiente
when @abajo; arregloTmp.last[1] -= @@anchoSerpiente
when @derecha; arregloTmp.last[0] += @@anchoSerpiente
when @izquierda; arregloTmp.last[0] -= @@anchoSerpiente
end
@@serpienteArr = arregloTmp
eliminarSerpiente
crearSerpiente
end
def eliminarSerpiente
@@serpienteActual.each do |ent|
ent.entities.each { |e| @@entidades.erase_entities(e) }
end
end
def finJuego
UI.stop_timer(@@idTemporizador)
UI.messagebox("¡Juego terminado!\nPuntuación: " + @@puntuacion.to_s)
actualizarPuntuacion
end
def actualizarPuntuacion
hash = {
apodo: config('apodo'),
puntuacion: @@puntuacion,
nivel: config('nivel'),
token: 'qwertyuioplkjhgfdsazxcvbnm'
}
solicitud = Sketchup::Http::Request.new("http://00a00.com/snake/guardar.php", Sketchup::Http::POST)
solicitud.body = construirQuery(hash)
solicitud.start do |req, res|
puts 'Ranking actualizado' if res
end
end
def construirQuery(hash)
cadena = ''
hash.each { |k, v| cadena += k.to_s + '=' + v.to_s + '&' }
cadena.chop!
end
def inicializarSerpiente(n)
arreglo = []
n.times do |i|
arreglo.push([@@anchoSerpiente/2 + @@anchoSerpiente*i, @@anchoSerpiente/2])
end
arreglo
end
end
end
Clase de la comida
# Comida
module SerpienteIngenua
class Comida < Base
def initialize
@posX = rand(-@@anchoMapa..@@anchoMapa) / 2 / 10 * 10
@posY = rand(-@@altoMapa..@@altoMapa) / 2 / 10 * 10
end
def mostrar
archivo = __dir__ + '/assets/comida.skm'
material = @@materiales.load(archivo)
puntos = []
unless estaLlena?
puntos[0] = [@posX, @posY, 0]
puntos[1] = [@posX+@@anchoSerpiente, @posY, 0]
puntos[2] = [@posX+@@anchoSerpiente, @posY+@@anchoSerpiente, 0]
puntos[3] = [@posX, @posY+@@anchoSerpiente, 0]
grupo = @@entidades.add_group
@@comida = [@posX+@@anchoSerpiente/2, @posY+@@altoSerpiente/2]
cara = grupo.entities.add_face(puntos)
cara.material = material
cara.reverse!.pushpull(@@altoSerpiente)
@comida = cara
else
@posX = rand(-@@anchoMapa..@@anchoMapa)
@posY = rand(-@@altoMapa..@@altoMapa)
end
end
def estaLlena?
@@serpienteArr.include?([@posX, @posY])
end
end
end