🤔 Para Refletir :
"Zzz... Zzzz... Opa! A cutscene já acabou?"
- Delayzado

Colisões por Hitbox e Quadtree

Brandt Masculino

Assistente
Colaboração
Colaboração
Quod erat demonstrandum
Juntou-se
22 de Junho de 2015
Postagens
336
Bravecoins
1.156
Point_quadtree.svg


Hitboxes | v0.1.0 | por Masked

HW2lYbO.png

para RPG Maker VX Ace

Requer o script DLL Utils.


Descrição

O script implementa classes auxiliares para sistemas de detecção de colisão via hitbox, inspirado nesse post do [member=1597]Jorge_Maker[/member].
Vale ressaltar que esse script não adiciona funcionalidades ao jogo por si só, apenas disponibiliza as ferramentas para que outros scripts o façam em seus próprios sistemas.

Inicialmente, o script era implementado puramente em Ruby. Conforme fui fazendo testes de performance, porém, ficou claro que o desempenho deixava a desejar, e como o intuito desse script é justamente ser eficiente, fiz o que precisava ser feito: implementei toda a lógica de colisão em C++, com paralelismo e tudo a que se tem direito, e botei numa DLL.

Esse script implementa duas classes:
  • Hitbox: Classe de hitbox. É basicamente um Rect que implementa funções de interseção.
  • Stage: Classe usada para colocar as hitboxes e testar colisão entre elas.



Instruções

Cole acima do script Main e abaixo do DLL Utils. Não esqueça de baixar a DLL disponível abaixo e salvar na pasta do projeto com o nome hitboxes.dll.




Exemplo de Uso

Segue código que faz uso de todos os recursos que o script oferece:
Código:
# Espaço para checagem de colisões
stage = Stage.new(Graphics.width, Graphics.height)

# Criamos 16 hitboxes de vários tamanhos em posições aleatórias
hitboxes = Array.new(16) do |i|
  Hitbox.new(rand(Graphics.width), rand(Graphics.height), 4 * i, 4 * i)
end

# Índice de hitboxes que colidiram, para colorir diferente
collided = {}

# Desenho na tela
screen = Sprite.new
screen.bitmap = Bitmap.new(Graphics.width, Graphics.height)

BLUE = Color.new(0, 0, 255)
RED = Color.new(255, 0, 0)

def update_hitbox(stage, hitbox)
  # Move a hitbox em direção ao centro da tela
  cx = Graphics.width / 2
  cy = Graphics.width / 2
  if cx != hitbox.rect.x
    t = Math.atan2(cy - hitbox.rect.y, cx - hitbox.rect.x)
    hitbox.move(Math.cos(t) * 2, Math.sin(t) * 2)
  end
  
  # No momento, o Stage não suporta hitboxes em movimento. Então tiramos
  # ela e colocamos de novo para atualizar a quadtree
  stage.delete(hitbox)
  stage.push(hitbox)
end

loop do
  screen.bitmap.clear
  
  for h in hitboxes
    # Desenha a hitbox na tela
    screen.bitmap.fill_rect(h.rect, collided[h.handle] ? BLUE : RED)
    
    # Atualiza o estado da hitbox
    update_hitbox(stage, h)
  end

  # Atualiza o estado das colisões
  stage.update
  
  # Sinaliza colisões
  for a, b in stage.collisions
    collided[a.handle] = true
    collided[b.handle] = true
  end
  
  Graphics.update
end



Observações

Esta é uma versão inicial do script e deve ser vista como prova de conceito.

Um problema grave da implementação atual é que a Quadtree não lida com mudança de posição das hitboxes, e deve ser reconstruída a todo frame.
Também pode ocorrer travamento do sistema por completo caso mais que 16 objetos estejam numa mesma posição no espaço. Isso acontece pela forma como a quadtree está implementada. Estou estudando uma forma de corrigir esse problema.

Ajustes voltados para a correção desses problemas serão feitos no futuro.


Download



Script

Ruby:
#==============================================================================
# Hitboxes | v0.1.0 | por Masked
#
# para RPG Maker VX Ace
#------------------------------------------------------------------------------
# Implementa hitboxes com posição e dimensão e operações básicas como colisão
# e movimento.
# Feito com intuito de servir de base para o desenvolvimento de outros
# scripts.
#==============================================================================
__END__ if ($modules ||= {})[:hitboxes]
$modules[:hitboxes] = 2.0
#==============================================================================
# ** Vector2D
#------------------------------------------------------------------------------
# Classe de vetor bidimensional
#==============================================================================
class Vector2D
  #--------------------------------------------------------------------------
  # * Atributos
  #--------------------------------------------------------------------------
  attr_reader :x, :y
  #--------------------------------------------------------------------------
  # * Construtor
  #--------------------------------------------------------------------------
  def initialize(x, y)
    @x = x
    @y = y
  end
  #--------------------------------------------------------------------------
  # * Construtor
  #--------------------------------------------------------------------------
  class << self
    alias [] new
  end
  #--------------------------------------------------------------------------
  # * Obtém a norma (comprimento) do vetor
  #--------------------------------------------------------------------------
  def norm
    Math.hypot(@x, @y)
  end
  #--------------------------------------------------------------------------
  # * Obtém a norma quadrada do vetor
  #--------------------------------------------------------------------------
  def norm2
    @x ** 2 + @y ** 2
  end
  #--------------------------------------------------------------------------
	# * Verifica igualdade entre vetores
	#--------------------------------------------------------------------------
	def ==(other)
		return false unless other.is_a? Vector2D
		self.x == other.x and self.y == other.y
  end
  #--------------------------------------------------------------------------
	# * Soma dois vetores
	#--------------------------------------------------------------------------
	def +(other)
		Vector2D.new(self.x + other.x, self.y + other.y)
	end
	#--------------------------------------------------------------------------
	# * Subtrai dois vetores
	#--------------------------------------------------------------------------
	def -(other)
		Vector2D.new(self.x - other.x, self.y - other.y)
	end
	#--------------------------------------------------------------------------
	# * Negativa o vetor
	#--------------------------------------------------------------------------
	def -@
		Vector2D.new(-self.x, -self.y)
	end
	#--------------------------------------------------------------------------
	# * Multiplica o vetor por um número
	#--------------------------------------------------------------------------
	def *(scale)
		Vector2D.new(self.x * scale, self.y * scale)
	end
	#--------------------------------------------------------------------------
	# * Divide o vetor por um número
	#--------------------------------------------------------------------------
	def /(scale)
		Vector2D.new(self.x / scale, self.y / scale)
	end
	#--------------------------------------------------------------------------
	# * Produto vetorial
	#--------------------------------------------------------------------------
	def dot(other)
		self.x * other.x + self.y * other.y
	end
	#--------------------------------------------------------------------------
	# * Módulo do produto em cruz
	#--------------------------------------------------------------------------
	def cross(other)
		self.x * other.y - self.y * other.x
  end
  #--------------------------------------------------------------------------
  # * Obtém um elemento no vetor por posição
  #--------------------------------------------------------------------------
  def [](i)
    to_a[i]
  end
  #--------------------------------------------------------------------------
  # * Converte o vetor em array
  #--------------------------------------------------------------------------
  def to_a
    [@x, @y]
  end
end
#==============================================================================
# ** Matrix2D
#------------------------------------------------------------------------------
# Classe de matrix 2x2
#==============================================================================
class Matrix2D
  #--------------------------------------------------------------------------
  # * Construtor
  #--------------------------------------------------------------------------
  def initialize(a, b, c, d)
    @values = [a, b, c, d]
  end
  #--------------------------------------------------------------------------
  # * Construtor
  #--------------------------------------------------------------------------
  class << self
    alias [] new
  end
  #--------------------------------------------------------------------------
  # * Obtém o valor na posição m, n da matriz
  #--------------------------------------------------------------------------
  def [](m, n)
    @values[m * 2 + n]
  end
  #--------------------------------------------------------------------------
  # * Obtém a determinante da matriz
  #--------------------------------------------------------------------------
  def determinant
    self[0, 0] * self[0, 1] - self[1, 0] * self[1, 1]
  end
  #--------------------------------------------------------------------------
	# * Verifica igualdade entre matrizes
	#--------------------------------------------------------------------------
	def ==(other)
		return false unless other.is_a? Matrix2D
		@values == other.instance_variable_get(:@values)
  end
	#--------------------------------------------------------------------------
  # * Mapeia cada valor da matriz usando uma função dada
	#--------------------------------------------------------------------------
  def map &block
    Matrix2D[*@values.map(&block)]
  end
	#--------------------------------------------------------------------------
  # * Mapeia cada valor da matriz com posição usando uma função dada
	#--------------------------------------------------------------------------
  def map_with_index &block
    a = @values.zip([0, 0, 1, 1], [0, 1, 0, 1]).map &block
    Matrix2D[*a]
  end
  #--------------------------------------------------------------------------
	# * Soma duas matrizes
	#--------------------------------------------------------------------------
	def +(other)
		map_with_index do |v, m, n|
      v + other[m, n]
    end
	end
	#--------------------------------------------------------------------------
	# * Subtrai dois vetores
	#--------------------------------------------------------------------------
	def -(other)
		map_with_index do |v, m, n|
      v - other[m, n]
    end
	end
	#--------------------------------------------------------------------------
	# * Negativa o vetor
	#--------------------------------------------------------------------------
	def -@
		map do |v|
      -v
    end
	end
	#--------------------------------------------------------------------------
	# * Multiplica o vetor por um número
	#--------------------------------------------------------------------------
	def *(other)
    if other.is_a? Numeric
      scale other
    elsif other.is_a? Matrix2D
      map_with_index do |v, m, n|
        self[m, 0] * self[0, n] + self[m, 1] * self[1, n]
      end
    elsif other.is_a? Vector2D
      Vector2D[
        self[0, 0] * other.x + self[0, 1] * other.y,
        self[1, 0] * other.x + self[1, 1] * other.y,
      ]
    else
      raise ArgumentError.new "Argument is neither Numeric, Matrix2D or Vector2D"
    end
  end
	#--------------------------------------------------------------------------
  # * Multiplica a matriz por um escalar
	#--------------------------------------------------------------------------
  def scale(x)
    map do |v|
      v * x
    end
  end
	#--------------------------------------------------------------------------
	# * Divide o vetor por um número
	#--------------------------------------------------------------------------
	def /(scale)
		map do |v|
      v / scale
    end
	end
	#--------------------------------------------------------------------------
  # * Retorna a transposta da matriz
	#--------------------------------------------------------------------------
  def transpose
    map_with_index do |v, m, n|
      self[n, m]
    end
  end
  #--------------------------------------------------------------------------
  # * Converte a matriz em array
  #--------------------------------------------------------------------------
  def to_a
    @values
  end
end
#==============================================================================
# ** Hitbox
#------------------------------------------------------------------------------
# Classe para caixa de colisão alinhada aos eixos (i.e. não rotacionada)
#==============================================================================
class Hitbox
  #--------------------------------------------------------------------------
  # * Atributos
  #--------------------------------------------------------------------------
  attr_reader :x, :y
  attr_reader :width, :height
  #--------------------------------------------------------------------------
  # * Construtor
  #     x       : Posição X da hitbox
  #     y       : Posição Y da hitbox
  #     width   : Largura da hitbox
  #     height  : Altura da hitbox
  #--------------------------------------------------------------------------
  def initialize(*args)
    if args[0].is_a? Rect
      rect = args[0]
    else
      rect = Rect.new(*args)
    end
    @x, @y, @width, @height = rect.x, rect.y, rect.width, rect.height
  end
  #--------------------------------------------------------------------------
  # * Posição XY do centro da hitbox
  #--------------------------------------------------------------------------
  def center
    [(left + right) / 2, (top + bottom) / 2]
  end
  #--------------------------------------------------------------------------
  # * Posição esquerda da hitbox
  #--------------------------------------------------------------------------
  alias left x
  #--------------------------------------------------------------------------
  # * Posição superior da hitbox
  #--------------------------------------------------------------------------
  alias top y
  #--------------------------------------------------------------------------
  # * Posição direita da hitbox
  #--------------------------------------------------------------------------
  def right
    x + width
  end
  #--------------------------------------------------------------------------
  # * Posição inferior da hitbox
  #--------------------------------------------------------------------------
  def bottom
    y + height
  end
  #--------------------------------------------------------------------------
  # * Retângulo da hitbox
  #--------------------------------------------------------------------------
  def rect
    Rect.new(x, y, width, height)
  end
  #--------------------------------------------------------------------------
  # * Converte a hitbox em array
  #--------------------------------------------------------------------------
  def to_a
    [left, top, right, bottom]
  end
  #--------------------------------------------------------------------------
  # * Soma um vetor à hitbox
  #--------------------------------------------------------------------------
  def +(vector)
    Hitbox.new(x + vector.x, y + vector.y, width, height)
  end
  #--------------------------------------------------------------------------
  # * Subtrai um vetor da hitbox
  #--------------------------------------------------------------------------
  def -(vector)
    Hitbox.new(x - vector.x, y - vector.y, width, height)
  end
  #--------------------------------------------------------------------------
  # * Vértices da hitbox em sentido horário
  #--------------------------------------------------------------------------
  def vertices
    [
      Vector2D[left,  top   ],
      Vector2D[right, top   ],
      Vector2D[right, bottom],
      Vector2D[left,  bottom]
    ]
  end
  #--------------------------------------------------------------------------
  # * Verifica se a hitbox contém um ponto
  #     px  : Coordenada X do ponto
  #     py  : Coordenada Y do ponto
  #--------------------------------------------------------------------------
  def contains?(*args)
    return contains_vector?(args[0]) if args[0].is_a? Vector2D
    px, py = *args
    px.between?(left, right) and py.between?(top, bottom)
  end
  #--------------------------------------------------------------------------
  # * Verifica se a hitbox contém um ponto representado por um vetor
  #     vector  : Vetor representando o ponto
  #--------------------------------------------------------------------------
  def contains_vector?(vector)
    contains? *vector
  end
  #--------------------------------------------------------------------------
  # * Verifica interseção com outra hitbox
  #--------------------------------------------------------------------------
  def intersects? other
    if other.is_a? RotatedHitbox
      other.intersects? self
    else
      right >= other.left and left <= other.right and
      bottom >= other.top and top <= other.bottom
    end
  end
end
#==============================================================================
# ** RotatedHitbox
#------------------------------------------------------------------------------
# Classe para caixa de colisão rotacionável
#==============================================================================
class RotatedHitbox < Hitbox
  #--------------------------------------------------------------------------
  # * Atributos
  #--------------------------------------------------------------------------
  attr_reader :angle
  #--------------------------------------------------------------------------
  # * Construtor
  #--------------------------------------------------------------------------
  def initialize(*args)
    return copy *args if args[0].is_a? Hitbox
    
    unless args.size == 5
      raise ArgumentError.new(
      "wrong number of arguments (given #{args.size}, expected 5)")
    end
     
    @x, @y, @width, @height = *args
    self.angle = args[4]
  end
  #--------------------------------------------------------------------------
  # * Define o ângulo da hitbox
  #--------------------------------------------------------------------------
  def angle=(angle)
    @angle = angle % (2 * Math::PI)
    @cos = Math.cos @angle
    @sin = Math.sin @angle
    @vertices = nil
  end
  #--------------------------------------------------------------------------
  # * Cria a partir de uma hitbox e rotaciona ela
  #     box   : Hitbox a ser copiada
  #     angle : Ângulo a rotacionar
  #--------------------------------------------------------------------------
  def copy(box, angle = 0)
    @x, @y, @width, @height = box.x, box.y, box.width, box.height
    @angle = angle
    @angle += @angle.angle if box.respond_to? :angle
  end
  #--------------------------------------------------------------------------
  # * Verifica se a hitbox contém um ponto
  #     px  : Coordenada X do ponto
  #     py  : Coordenada Y do ponto
  #--------------------------------------------------------------------------
  def contains?(px, py)
    a, b, c, d = *vertices
    p = Vector2D[px, py]
    
    ad = a - d
    cd = c - d
    tpc = p * 2 - a - c
    
    cd.dot(tpc - cd) <= 0 and cd.dot(tpc + cd) >= 0 and
    ad.dot(tpc - ad) <= 0 and ad.dot(tpc + ad) >= 0
  end
  #--------------------------------------------------------------------------
  # * Verifica interseção com outra hitbox
  #--------------------------------------------------------------------------
  def intersects?(other)
    other.vertices.any? {|p| self.contains? p } or
    self.vertices.any? {|p| other.contains? p }
  end
  #--------------------------------------------------------------------------
  # * Vértices da hitbox
  #--------------------------------------------------------------------------
  def vertices
    return @vertices if @vertices
    
    origin = self.center()
    @vertices = super.map do |v|
      origin + rotation_matrix * (v - origin)
    end
    
    @vertices
  end
  #--------------------------------------------------------------------------
  # * Matriz de rotação relacionada à rotação da hitbox
  #--------------------------------------------------------------------------
  def rotation_matrix
    Matrix2D[
      @cos, -@sin,
      @sin, @cos
    ]
  end
end
#==============================================================================
# ** Quadtree
#------------------------------------------------------------------------------
# Implementação de PR (Point-Region) quadtree, para implementar detecção de
# colisões eficiente entre hitboxes
#==============================================================================
class Quadtree
  #--------------------------------------------------------------------------
  # * Constantes
  #--------------------------------------------------------------------------
  NODE_CAPACITY = 8
  #--------------------------------------------------------------------------
  # * Atributos
  #--------------------------------------------------------------------------
  attr_reader :boundary
  attr_reader :objects
  attr_reader :children
  #--------------------------------------------------------------------------
  # * Construtor
  #--------------------------------------------------------------------------
  def initialize(*args)
    @boundary = Hitbox.new(*args)
    @objects = []
    @children = nil
  end
  #--------------------------------------------------------------------------
  # * Verifica se a árvore é folha (i.e. não tem filhos)
  #--------------------------------------------------------------------------
  def leaf?
    children.nil?
  end
  #--------------------------------------------------------------------------
  # * Insere um ponto na árvore
  #--------------------------------------------------------------------------
  def push(hitbox)
    return false unless hitbox.intersects? @boundary
    if objects.size < NODE_CAPACITY and leaf?
      objects << hitbox
      return true
    end
    subdivide if leaf?
    for c in children
      c.push hitbox
    end
  end
  alias << push
  #--------------------------------------------------------------------------
  # * Subdivide a árvore em quadrantes
  #--------------------------------------------------------------------------
  def subdivide
    l, t, r, b = *boundary.to_a
    cx, cy = *boundary.center
    @children = [
      Quadtree.new(l, t, cx - 1 - l, cy - 1 - t),
      Quadtree.new(cx, t, r - cx, cy - 1 - t),
      Quadtree.new(cx, cy, r - cx, b - cy),
      Quadtree.new(l, cy, cx - 1 - l, b - cy),
    ]
  end
  #--------------------------------------------------------------------------
  # * Obtém as hitboxes que possivelmente colidem com uma hitbox dada
  #--------------------------------------------------------------------------
  def near(hitbox)
    return [] unless hitbox.intersects? @boundary
    result = objects.select {|h| hitbox.intersects? h}
    return result if leaf?
    @children.inject(result) {|r, tree| r.concat tree.near hitbox }
  end
end


Licensa: Creative Commons Attribution-ShareAlike 4.0 International
 
Sobre os problemas de performance que mencionou, isso tem ocasionado muitas quedas de frames ou algo do tipo (não cheguei a testar ainda)?
Isso abre um leque de opções, com esse tipo de sistema dá para bolar jogos mais complexos que os RPGs padrões da engine ou mesmo sistemas diferenciados para o mesmo (uns beat'em ups da vida quem sabe...).
Boa fióti!
 
Então, consegui fazer um teste rodar liso com 200 hitboxes rotacionadas se movendo (ou seja, recriando a Quadtree toda a cada frame) na tela, e isso mostrando as hitboxes como sprites. Vale notar que hitboxes rotacionadas são mais difíceis de identificar colisão (envolve até multiplicação de matrizes), então com hitboxes alinhadas provavelmente dá pra alargar esse número. Se não mostrar as hitboxes (que é bem comum em jogos), então tem mais uma margem aí.

Pra colocar em perspectiva, o mesmo teste com 100 hitboxes sem usar a Quadtree já joga o fps pra por volta de 30. Então não é bem que o script tenha problemas de performance, ele só não é tão eficiente quanto poderia, creio kk

Idealmente, o script deve permitir pelo menos umas mil hitboxes na tela sem grande perda de framerate, até porque a diferença de verdade nesse algoritmo é a complexidade dele: O algoritmo ingênuo, que verifica colisão pra cada par de hitboxes, roda em tempo proporcional ao quadrado da quantidade de hitboxes (n²), já esse roda proporcionalmente ao produto do número de hitboxes e seu logarítmo (n log n). Com isso, se tivermos 1000 hitboxes, o algoritmo mais simples faria 1 milhão de comparações, enquanto o da quadtree faria por volta de 5000 (ooooh).

Pra melhorar isso eu preciso dar um jeito de atualizar a árvore para hitboxes em movimento sem ter que reconstruir ela a todo frame, que gera um gasto desnecessário de memória e tempo, e dar uma olhada em como melhorar a estrutura da quadtree pra diminuir mais ainda a quantidade de comparações. (É a primeira vez que implemento uma dessas, tô meio confuso ainda rs).
Em último caso, eu implemento a parte pesada em C e faço uma DLL que dê pra puxar as coisas, porque a ideia é fazer esse negócio ficar turbinado mesmo xd

Sobre novas possibilidades de jogos, sim! Muito!
Fiz um bullet hell faz um tempo em javascript e estava super a fim de refazer no VX Ace, e espero que esse sistema me dê uma performance boa pra dar pra fazer o que eu preciso sem ter que me preocupar com queda de frames e tal.
 
[member=78]Brandt[/member]
Esse sistema poderia ter algum tipo de sinergia com algum Pixel Movement por exemplo?
Existe uma versão de Touhou para o RPG maker VX que modificou muita coisa (é um jogo de "plataforma-bullet hell"), essas adições de classes fariam coisas do tipo?
Muito interessante, parabéns!
 
[member=962]Jully Anne[/member]

Então, muito provavelmente sistemas de pixel movement poderiam tranquilamente aproveitar esse sistema pro sistema de colisão, sim!
Uma ressalva que eu colocaria nesse aspecto, porém, é que nem sempre adicionar a Quadtree vai trazer ganho à performance. Apesar da minha inspiração ter sido justamente esse tipo de sistema, acho que na realidade pixel movement em específico pode acabar sendo pouco beneficiado com o script.

Isso depende muito da quantidade de objetos que vão ser checados por colisão, e se parar pra pensar, pro pixel movement podemos contar apenas os objetos que aparecem na tela, e esses dificilmente passam de algumas dezenas.

Já pra jogos no estilo bullet hell, sim, é bem capaz que usar esse sistema possibilite fazer jogos com um montão de tiro na tela! :D

Minha perspectiva pro futuro pra esse script é acabar implementando uma engine de física mais completa, que já traga mais features pra manipular corpos rígidos e afins (até por isso as classes de vetor e matriz).
 
[member=78]Brandt[/member]

Muito bom!!

Eu também adoro o RPG Maker VX/Ace, tenho no papel um projeto que o seu script cairia como uma luva.

Você pretende utilizar o script em um projeto futuro? Adoraria ver todo o potencial desse script em ação.
 
[member=770]Resque[/member]

Gostaria de reimplementar o bullet hell que fiz um tempo atrás usando isso aí sim xd
Bom saber que o script pode ser útil, só cuidado pra não pegar uma versão muito beta e acabar tendo problemas auheuahea

No momento eu não acho que o script está pronto pra uso, vai precisar de alguns bons ajustes antes.

Aliás...



Versão 0.2.0 - Changelog

Não deu, o Ruby estava limitando demais a performance do sistema. Transferi toda a lógica para C++ e coloquei numa DLL, disponível pra download no tópico.

Com isso, temos algumas mudanças significativas:

- Remoção da classe RotatedHitbox. Não que não vá ter, é só que não tive tempo pra implementar ela em C++, e tenho que planejar direitinho como vou colocar ela lá pra não ter mais problemas.

- Bug monstro na quadtree. E não é feature: quando tem mais que 16 objetos juntos no espaço na mesma quadtree, ela entra em recursão infinita. Isso acontece porque cada quatree aguenta no máximo 16 objetos (dá pra mexer nisso, mas afeta a performance e não resolve o problema) antes de se subdividir, mas se os 16 objetos estão na mesma posição a quadtree fica se dividindo indefinidamente, sem nunca conseguir dividir os objetos em árvores diferentes. Isso pode ser resolvido com um valor de profundidade máxima para a árvore ou subdividindo as hitboxes na hora de armazenar elas, mas vou estudar melhor qual a melhor solução.

- Performance absurda. Fora os problemas, tem esse ponto, que acho que faz o resto valer a pena: dá pra rodar colisões entre 2600 hitboxes a 60 fps!! Iterando por todas as colisões, porém, existe um overhead do ruby e das chamadas da API, e assim o limite cai pra umas 600 hitboxes, que ainda é 3x mais que o teste anterior.

- Paralelismo. A detecção de colisões usa multithreading para paralelizar o trabalho de detectar as interseções das hitboxes, se possível. O algoritmo determina a quantidade de cores disponíveis na máquina e faz uso do máximo deles para dividir a carga entre eles. Como o meu processador tem 4 cores, dá pra estimar (bem por cima) que em um computador 16 core o script potencialmente suporta por volta de 10000 hitboxes a 60 fps sem contar a iteração, o que é bem incrível kk.

A DLL está disponível para download no tópico junto do código fonte.

\o
 
Voltar
Topo Inferior