¡Bienvenido a Universo Maker!
¿Es esta la primera vez que visitas el foro? Si es así te recomendamos que te registres para tener completo acceso a todas las áreas y secciones del foro, así también podrás participar activamente en la comunidad. Si ya tienes una cuenta, conectate cuanto antes.
Iniciar Sesión Registrarme

No estás conectado. Conéctate o registrate

Ver el tema anterior Ver el tema siguiente Ir abajo Mensaje [Página 1 de 1.]

#1 [XP] Emisor de partículas el Mar Ago 14, 2018 10:53 pm

Reputación del mensaje :100% (3 votos)

orochii

avatar

Emisor de partículas




(a veces llamado también "sistema de partículas)
Versión 0.5a, testeado en RGSS1
(falta probar si funciona en RGSS2/3 en teoría debería)

Introducción

Este script consiste en las clases necesarias para crear un emisor de partículas, o sistema de partículas, en la pantalla. Por el momento está sólo el emisor, no hay implementación hecha en mapa ni batalla ni nada. Pero un scripter puede usar esto y ponerlos en pantalla igual que usar un objeto Sprite normal.

Falta probar si funciona en RMVX y Ace, pero en teoría funciona. No usa nada específico de ninguno, simplemente clases Sprite y tal. El asunto es que lo que sigue (implementar para usar en batalla y mapa) sí debe ser hecho para cada RPG Maker específicamente, por lo que primero pongo esto a ver si alguien quiere ayudar con testing (necesita saber scriptear).

Muestra



Script

Código:
=begin
================================================================================
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
OZ Particle Emitter - Versión 0.5a
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Testeado en RGSS1
Autor: Orochii Zouveleki

Documentación:
''''''''''''''
module OZMath
  Este módulo incluye algunas operaciones comunes.

  def self.deg2rad(degrees) - Convierte grados a radianes.
 Retorno: Numeric.
  def self.lerp(v,a,b) - Interpolación lineal entre números a y b
 de acuerdo a v (0..1).
 Retorno: Numeric.
  def self.clamp(v,min,max) - Restringe un valor v a un intérvalo [min,max]
 Retorno: Numeric.
  def self.rand_range(a,b) - Número aleatorio entre a y b.
 Retorno: Numeric.
  def self.rand_range_i(a,b) - Número aleatorio entero entre a y b.
 Retorno: Numeric.
  def self.lerp_col(v, c1, c2) - Interpolación lineal entre Color c1 y c2
 de acuerdo a v.
   Retorno: Color.

class FreeRange
 Esta clase es una versión inútil de Range, con el objeto de soportar
 valores de punto flotante.
 Simplemente necesitaba algo que lo guardara, y no fuera Array. ¯\_(ツ)_/¯

  def initialize(first,last) -Inicializa objeto
  attr_reader :first -Valor inicial
  attr_reader :last -Valor final

class Particle < Sprite
  attr_reader :dead
  def initialize(bitmap,emissor,x,y,viewport=nil)
 -Inicialización partícula
  def update -Lógica de partícula
  def get_property(p,modifier=nil,k=:number)
 -Obtiene valor real de propiedad
   Retorno: Numeric, Array de Numeric o Color
  def get_max(p, k=:number) -
   Retorno: Numeric o Array de Numeric
  def get_modifier(mod)
     Retorno: Numeric 0..1
  # Métodos de utilidad internos
  def iter_modifiers(m, lm, sm)
  def get_property_color(p,modifier)
  def get_property_array(p,modifier)
  def get_property_number(p,modifier)
  def get_max_array(p)
  def get_max_number(p)

class ParticleEmissorProperties
  
  # Global attributes
  attr_accessor :viewport -Viewport usado por todos los sprites
  attr_accessor :simulation_space -:local para mover partícula con emisor
  attr_accessor :max_particles -Numeric, límite de sprites
  attr_accessor :duration -frames antes de reinicio de emisión
  attr_accessor :looping -si se repite el efecto
  attr_accessor :autoplay -emitir al iniciar
  attr_accessor :bitmaps -imágenes usadas por partículas (al azar)
  
  # Emission attributes
  attr_accessor :pps -Partículas por segundo
  attr_accessor :bursts -Array de ráfagas. Usa un tiempo t de
 acuerdo al temporizador interno del emisor
 y un número n de partículas a emitir en
 el momento. bmp es usado para determinar
 un bitmap personalizado (nil para usar
 los otros al azar).
 [[t1,n1,bmp],[t2,n2,bmp], (...)]
  attr_accessor :shape -Forma del emisor. :circle o :square
  attr_accessor :shape_a -Radio mínimo para :circle. Ancho para :square
  attr_accessor :shape_b -Radio máximo para :circle. Alto para :square
  attr_accessor :shape_angle -En círculos, delimita el arco de efecto.
 [anguloInicio,anguloFin,angleStep]
  
  # Particle attributes
 Los atributos de partícula suelen poseer un atributo modificador que modifica su
 comportamiento de acuerdo a otro valor.
 Ej. Si speed_modifier==:lifetime, la velocidad cambiará a lo largo de
 la vida de la partícula.
 Los atributos además pueden recibir valores en arrays o sueltos, así como rangos.
 Los rangos pueden ser clase Range o FreeRange (clase hecha como parte de este script).
 Ejs.
 color = Color.new(0,0,0,0)
 color = [Color.new(0,0,0,255),Color.new(32,128,196,160)]
 speed = [1,0]
 speed = [[-1,5,7],FreeRange.new(-3,3)]
 Algunos atributos requieren ser encapsulados en un array siempre de un tamaño específico,
 pero sus miembros internos pueden encapsularse en otro array o ser Range/FreeRange.
 
  attr_accessor :lifetime -Tiempo de vida de partículas (en frames)
  attr_accessor :speed -Velocidad [X,Y].
  attr_accessor :speed_modifier # :none AZAR :speed VELOCIDAD :lifetime VIDA RESTANTE
  attr_accessor :acceleration -
  attr_accessor :acceleration_modifier # :none AZAR :speed VELOCIDAD :lifetime VIDA RESTANTE
  attr_accessor :size -
  attr_accessor :size_modifier # :none AZAR :speed VELOCIDAD :lifetime VIDA RESTANTE
  attr_accessor :rotation -
  attr_accessor :rotation_modifier # :none AZAR :speed VELOCIDAD :lifetime VIDA RESTANTE
  attr_accessor :opacity -
  attr_accessor :opacity_modifier # :none AZAR :speed VELOCIDAD :lifetime VIDA RESTANTE
  attr_accessor :color -
  attr_accessor :color_modifier # :none AZAR :speed VELOCIDAD :lifetime VIDA RESTANTE
  def initialize(bitmaps=[]) -Inicializador, recibe bitmaps a usar.
  def get_random_bitmap -Devuelve un bitmap al azar de bitmaps.
 Devuelve un bitmap blanco de 8x8 si no hay bitmaps.
  
class ParticleEmissor
  attr_accessor :properties
  attr_accessor
  attr_accessor :y
  def initialize(x, y, z, _properties=ParticleEmissorProperties.new)
  def update
  def create_new_particle(bmp)
  def get_shape_coordinate
  def dispose
================================================================================
=end

module OZMath
  def self.deg2rad(degrees)
    return degrees * Math::PI / 180
  end
  
  def self.lerp(v,a,b)
    return (b-a)*v + a
  end
  
  def self.clamp(v,min,max)
    return [ [ min, v ].max, max ].min
  end
  
  def self.rand_range(a,b)
    return rand * (b-a) + a
  end
  
  def self.rand_range_i(a,b)
    return rand(b-a)+a
  end
  
  def self.lerp_col(v, c1, c2)
    r = lerp(v, c1.red, c2.red)
    g = lerp(v, c1.green, c2.green)
    b = lerp(v, c1.blue, c2.blue)
    a = lerp(v, c1.alpha, c2.alpha)
    return Color.new(r,g,b,a)
  end
end

class FreeRange
  def initialize(first,last)
    @first = first
    @last = last
  end
  
  attr_reader :first
  attr_reader :last
end

class Particle < Sprite
  attr_reader :dead
  
  def initialize(bitmap, emissor, x, y, viewport=nil)
    super(viewport)
    self.bitmap = bitmap
    self.blend_type = 1
    @ref = emissor if emissor.properties.simulation_space==:local
    @x = x
    @y = y
    if @ref==nil
      @x += emissor.x
      @y += emissor.y
    end
    # Lifetime is constant
    @lifetime = get_property(emissor.properties.lifetime)
    @starting_lifetime = @lifetime
    @dead = false
    # Modifiers are also constant
    @speed_modifier = emissor.properties.speed_modifier
    @acceleration_modifier = emissor.properties.acceleration_modifier
    @size_modifier = emissor.properties.size_modifier
    @rotation_modifier = emissor.properties.rotation_modifier
    @opacity_modifier = emissor.properties.opacity_modifier
    @color_modifier = emissor.properties.color_modifier
    
    # Others are processed
    @speed = emissor.properties.speed
    @top_speed = get_max(emissor.properties.speed,:array)
    @acceleration = emissor.properties.acceleration
    @size = emissor.properties.size
    @rotation = emissor.properties.rotation
    @opacity_ = emissor.properties.opacity
    @color = emissor.properties.color
    # Initialize speed
    if @speed_modifier==:lifetime
      @current_speed = get_property(@speed,0,:array)
    else
      @current_speed = get_property(@speed,nil,:array)
    end
    # Update
    update
  end
  
  def iter_modifiers(m, lm, sm)
    return (m==:lifetime) ? lm : (m==:speed) ? sm : nil
  end
  
  def update
    super
    # Lifetime update
    return if @dead==true
    @lifetime -= 1
    
    # Buffer modifiers
    lm = get_modifier(:lifetime)
    sm = get_modifier(:speed)
    # Set modifier buffers to each
    speed_mod = iter_modifiers(@speed_modifier,lm,sm)
    accel_mod = iter_modifiers(@acceleration_modifier,lm,sm)
    size_mod = iter_modifiers(@size_modifier,lm,sm)
    rot_mod = iter_modifiers(@rotation_modifier,lm,sm)
    opacity_mod = iter_modifiers(@opacity_modifier,lm,sm)
    color_mod = iter_modifiers(@color_modifier, lm, sm)
    # Update speed
    accel = get_property(@acceleration,accel_mod,:array)
    @current_speed[0] += accel[0]
    @current_speed[1] += accel[1]
    # Update size
    self.zoom_x = get_property(@size[0],size_mod) if size_mod != nil
    self.zoom_y = get_property(@size[1],size_mod) if size_mod != nil
    # Update angle
    self.angle += get_property(@rotation,rot_mod)
    self.opacity = get_property(@opacity_,opacity_mod)
    # Update color
    self.color = get_property(@color, color_mod, :color)
    # Update position
    sm = speed_mod==nil ? 1 : speed_mod
    @x += @current_speed[0]*sm
    @y += @current_speed[1]*sm
    if @ref==nil
      self.x = @x
      self.y = @y
    else
      self.x = @x + @ref.x
      self.y = @y + @ref.y
    end
    
    if (@lifetime <= 0)
      @dead = true
      self.visible = false
    end
  end
  
  # Returns: Any number / array of number
  def get_property(p,modifier=nil,k=:number)
    return get_property_color(p, modifier) if k==:color
    return get_property_array(p, modifier) if k==:array
    return get_property_number(p,modifier)
  end
  # Returns: Any number / array of number
  def get_max(p, k=:number)
    return get_max_array(p) if k==:array
    return get_max_number(p)
  end
  # Returns: 0..1
  def get_modifier(mod)
    if mod==:lifetime
      return (@starting_lifetime-@lifetime)*1.0/@starting_lifetime
    end
    if mod==:speed
      s = @current_speed[0].abs + @current_speed[1].abs
      ts= @top_speed[0].abs + @top_speed[1].abs
      return (s*1.0/ts)
    end
    return 0
  end
  
  # "HELPERS" (or internal methods)
  def get_property_color(p,modifier)
    if p.is_a?(Array)
      m = modifier==nil ? rand() : modifier
      a = (p.size * m).floor
      a = p.size-1 if a>=p.size
      b = a+1
      b = a if b>=p.size
      c1 = p[a]
      c2 = p[b]
      l = (m * p.size) - a
      return OZMath.lerp_col(l, c1, c2)
    elsif p.is_a?(Color)
      return p
    end
    return Color.new(0,0,0)
  end
  def get_property_array(p,modifier)
    val = []
    for i in 0...p.size
      val[i] = get_property_number(p[i], modifier)
    end
    return val
  end
  def get_property_number(p,modifier)
    # If modifier set to none
    if (modifier == nil)
      if p.is_a?(Numeric)||p.is_a?(Color)
        return p
      elsif p.is_a?(Array)
        return 0 if p.size==0
        a = rand(p.size)
        return p[a]
      elsif p.is_a?(Range) || p.is_a?(FreeRange)
        return OZMath.rand_range(p.first, p.last)
      end
    end
    # Modifier must be 0..1
    if p.is_a?(Numeric)
      return p * modifier
    elsif p.is_a?(Array)
      return 0 if p.size==0
      a = (p.size * modifier).floor
      a = OZMath.clamp(a, 0, p.size-1)
      return p[a]
    elsif p.is_a?(Range) || p.is_a?(FreeRange)
      return OZMath.lerp(modifier, p.first, p.last)
    end
  end
  
  
  def get_max_array(p)
    val = []
    for i in 0...p.size
      val[i] = get_max_number(p[i])
    end
    return val
  end
  def get_max_number(p)
    if p.is_a?(Numeric)
      return p
    elsif p.is_a?(Array)
      a = 0
      p.each {|v| a = v if a<v}
      return a
    elsif p.is_a?(Range) || p.is_a?(FreeRange)
      return p.last
    end
  end
  
end

class ParticleEmissorProperties
  # Global attributes
  attr_accessor :viewport
  attr_accessor :simulation_space
  attr_accessor :max_particles
  attr_accessor :duration
  attr_accessor :looping
  attr_accessor :autoplay
  attr_accessor :bitmaps
  
  # Emission attributes
  attr_accessor :pps #Particles Per Second
  attr_accessor :bursts #Array [[t1,n1],[t2,n2]]
  attr_accessor :shape # :circle :square
  attr_accessor :shape_a
  attr_accessor :shape_b
  attr_accessor :shape_angle #circle only, [a,b,c]
  
  # Particle attributes (can receive array, range, etc)
  attr_accessor :lifetime
  attr_accessor :speed
  attr_accessor :speed_modifier # :none :speed :lifetime
  attr_accessor :acceleration
  attr_accessor :acceleration_modifier # :none :speed :lifetime
  attr_accessor :size
  attr_accessor :size_modifier # :none :speed :lifetime
  attr_accessor :rotation
  attr_accessor :rotation_modifier # :none :speed :lifetime
  attr_accessor :opacity
  attr_accessor :opacity_modifier # :none :speed :lifetime
  attr_accessor :color
  attr_accessor :color_modifier # :none :speed :lifetime
  
  def initialize(bitmaps=[])
    @viewport = nil
    @simulation_space = :global
    @max_particles = 1000
    @duration = 200
    @looping = true
    @autoplay = true
    
    @pps = 24
    @bursts = []
    @shape = :circle
    @shape_a = 8
    @shape_b = 0
    @shape_angle = [0,360,0]
    
    @lifetime = 60
    @speed = [FreeRange.new(-1,1),FreeRange.new(-1,1)]# ??
    @speed_modifier = :none
    @acceleration = [0,0]
    @acceleration_modifier = :none
    @size = [FreeRange.new(0.5,1.0),FreeRange.new(0.5,1.0)]
    @size_modifier = :none
    @rotation = 2
    @rotation_modifier = :none
    @opacity = FreeRange.new(192,32)
    @opacity_modifier = :lifetime
    @color = [Color.new(255,255,255,255),
              Color.new(255,255,0,255),
              Color.new(0,0,255,255)]
    @color_modifier = :lifetime
    @bitmaps = bitmaps
  end
  
  def get_random_bitmap
    if @bitmaps.size==0
      b = Bitmap.new(8,8)
      b.fill_rect(b.rect, Color.new(255,255,255,255))
      @bitmaps[0] = b
      return b
    end
    return @bitmaps[rand(bitmaps.size)]
  end
end

class ParticleEmissor
  attr_accessor :properties
  attr_accessor
  attr_accessor :y
  
  def initialize(x, y, z, _properties=ParticleEmissorProperties.new)
    @particles = []
    @properties = _properties
    @playing = @properties.autoplay
    @timer = @properties.duration
    @x = x
    @y = y
    @z = z
    @fps = @pps = 0 # Particle creation control variables
  end
  
  def update
    # Create control variables
    to_delete = []
    count = 0
    # Update existing particles.
    @particles.each {|p|
      if p.dead==true
        to_delete.push(p)
      else
        count += 1
        p.update
      end
    }
    # Remove old particles from array
    @particles = (@particles-to_delete)
    
    # Playing particle effect (creates new particles if it's playing)
    return if (!@playing)
    @timer -= 1
    if @timer <= 0
      if @properties.looping
        @timer = @properties.duration
      else
        @playing = false
      end
    end
    return if count > @properties.max_particles
    
    # Current second particles
    @fps += 1
    if @fps > Graphics.frame_rate
      @pps = @fps = 0
    end
    expected_pps = @properties.pps * @fps / Graphics.frame_rate
    particles_to_create = expected_pps - @pps
    
    # TODO - Bursts
    curr_time = @properties.duration - @timer
    @properties.bursts.each { |burst|
      if burst[0]==curr_time
        burst[1].times {|n| create_new_particle(burst[2])}
      end
    }
    
    if particles_to_create > 0
      # Create particle x times
      particles_to_create.times {|n| create_new_particle(nil)}
    end
    @pps = expected_pps
  end
  
  def create_new_particle(bmp)
    return if @particles.size >= @properties.max_particles
    # Create new particles
    if bmp==nil
      b = properties.get_random_bitmap
    else
      b = bmp
    end
    coord = get_shape_coordinate
    p = Particle.new(b, self, coord[0], coord[1], @viewport)
    p.z = @z
    @particles.push(p)
  end
  
  def get_shape_coordinate
    coord = [0,0]
    case @properties.shape
    when :circle
      _ap = @properties.shape_angle
      angle = OZMath.rand_range(_ap[0], _ap[1])
      angle = (angle / _ap[2]).floor * _ap[2] if _ap[2] > 0
      radius = OZMath.rand_range(@properties.shape_a, @properties.shape_b)
      rad = OZMath.deg2rad(angle)
      coord[0] = Math.cos(rad)*radius
      coord[0] = Math.sin(rad)*radius
    when :square
      ah = @properties.shape_a/2
      bh = @properties.shape_b/2
      coord[0] = OZMath.rand_range(-ah,ah)
      coord[1] = OZMath.rand_range(-bh,bh)
    end
    return coord
  end
  
  def dispose
    @particles.each {|p| p.dispose }
  end
end
Descargar demo (Particles.7z, ~200Kb)
Descargar script (Particles.rb, 17Kb)

Licencia

Creative Commons 0. Similar a dominio público, puede usarse, modificarse y distribuirse libremente, tanto para fines comerciales como no comerciales.

#2 Re: [XP] Emisor de partículas el Miér Ago 15, 2018 9:29 pm

Kululu

avatar
Bonito sistema @orochii, ojalá alguien aproveche tu base para desarrollar algo interesante.

Ópalo Reputación



     

¡Visita mis galerías de Tumblr y DeviantArt!

#3 Re: [XP] Emisor de partículas el Jue Ago 16, 2018 1:36 am

orochii

avatar
Pueden postear sus ideas, supongo, sobre cosas en las que piensen que podrían usar partículas. En general se pueden usar en cualquier lugar donde puedas tirar un sprite xD.

(Por supuesto yo decido si hacerlo xDDDD).

Ideas por ahora:
- Partículas en mapa. Dos tipos, scroll con mapa y fijas en pantalla (para menús personalizados y por el estilo).
- Partículas en animación de batalla.
- Cursor de ventana con partículas.

Ninguna de esas cosas es difícil por sí sola, pero considerar que tengo que hacerlo 3 veces :ribbumingface: pues me da un poco pereza xD. Odio repetir cosas.

Por otro lado, aquí les paso el "roadmap" para la clase principal, que es por lo que aún es 0.5a y no 1.0:

- Soporte para detener y pausar el efecto.
- Loop points. Esto permitiría tanto hacer un efecto de entrada como salida al efecto.
- ¿Les parece reproducción de audio?
- Editor de partículas. La idea es facilitar su uso y personalización y hacerlo más visual.

Como ven aún faltan muchas cosas :'D.

#4 Re: [XP] Emisor de partículas el Jue Ago 16, 2018 5:14 am

liendre

avatar
nice orocho, me la baje de intruso pero recorde que no tengo xp jaja.
que alguien lo tome y lo vuelva en nuevo trapcode del maker~
gud job.

en cuanto a ideas.
he estado mirando esto;

lo estuve amoldando y lo converti con su canal alfa respectivamente.
el punto es que son muchas imagenes para actualizar el efecto frame por frame.
seria nice poder lograr algo tal con jugando con las opacidades.
te dejo la idea por que es lo unico que puedo dejar :c

#5 Re: [XP] Emisor de partículas el Jue Ago 16, 2018 7:20 pm

orochii

avatar
Lo que llevo funciona en todos (digo, del XP al VXAce), acabo de probarlo en VXAce y funciona tal como pensaba. .-.

La muestra, creo que se puede hacer, tengo una idea de cómo hacerla. Cuando haga ejemplos de efectos con partículas intentaré hacer una parecida. No quedará igual igual xD pero bueno. Básicamente es jugar con escalas, spawnear partículas en toda la pantalla, y jugar también con aceleración y velocidad inicial.

Salut! C:

#6 Re: [XP] Emisor de partículas el Sáb Sep 01, 2018 12:01 am

Vala

avatar
¡Genial, Orochin, buen aporte!

Ya puedo imaginármelo funcionando: una antorcha en el interior de una pirámide, una fogata en medio de un bosque, una fuente en el centro de la ciudad...

Ópalo Reputación ¡Gracias!

Saludos,
Vala.



Contenido patrocinado


Ver el tema anterior Ver el tema siguiente Volver arriba  Mensaje [Página 1 de 1.]

Permisos de este foro:
No puedes responder a temas en este foro.