Legacy Engine - Controle de Animação
por Victor Sant
Não postar este script em nenhuma outra comunidade sem autorização
Scripts RGSS, Resources, Tutorials and Translations by Victor Sant is licensed under a
Creative Commons Atribuição-Uso Não-Comercial-Compartilhamento pela mesma Licença 2.5 Brasil License.
Permissions beyond the scope of this license may be available at Santuário RPG Maker
Legacy Engine
Opor Victor Sant
Não postar este script em nenhuma outra comunidade sem autorização
Scripts RGSS, Resources, Tutorials and Translations by Victor Sant is licensed under a
Creative Commons Atribuição-Uso Não-Comercial-Compartilhamento pela mesma Licença 2.5 Brasil License.
Permissions beyond the scope of this license may be available at Santuário RPG Maker
Legacy Engine
que é o Legacy Engine? O Legacy Engine é um conjunto de scripts com
funções váriadas para personalização de projetos. No geral são scripts
independentes que funcionam sozinhos (exceto pelos scripts 'base' que
são requisitos para o funcionamento deles), mas que podem ser usados
juntos com total compatibilidade, claro que algumas funções não podem
ser usadas em conjuto, porém não ocorrerão erros de incopatibilidade.
Introdução
Este
script permite fazer mudanças de pose e animações usando charsets de
forma simples e rápida: usando apenas um comando chamar script.
Isso torna muito mais facil criar animações suaves para poses diferenciadas sem precisar criar longas sequencias com eventos.
Características
• Requer o script Legacy Engine - Básico (Mapa)
• Exibir animações com charset de forma simples
Screenshots
Como usar
Para instalá-lo, apenas cole o script acima do script Main.
Deixe-o sempre abaixo do script 'Legacy Engine - Básico (Mapa)'
Se
usado junto de outros scripts do Legacy Engine, verifique a ordem
correta dos scripts no script 'Legacy Engine - Básico (Mapa)'
Scripts que não façam parte do 'Legacy Engine' devem ficar acima do script 'Legacy Engine - Básico (Mapa)'
Demais intruções de uso no cabeçalho do script.
Demo
Legacy Engine - Controle de Animação
Script
- Código:
#==============================================================================
# Legacy Engine - Controle de Animação
# por Atoa
#==============================================================================
# Atualizações
# v 1.0 - 05 / 07 / 2011 - Script Lançado
# v 1.1 - 15 / 07 / 2011 - Correção de Bug na execução de animações
# v 1.2 - 05 / 10 / 2011 - Adição dos comando de mudança de estado (stance)
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Este script permite fazer mudanças de pose e animações usando charsets
# de forma simples e rápida: usando apenas um comando chamar script.
# Isso torna muito mais facil criar animações suaves para poses diferenciadas
# sem precisar criar longas sequencias com eventos.
#
# Pose de Movimento:
# Por padrão o script usa um gráfico diferenciado para as poses de repouso e
# de movimento. O gráfico da pose de movimento deve possuir o sufixo [wlk].
# Saltos também possuem um gráfico específico, use o sufixo [jmp]
# Se usando o script 'LE | Corrida', a corrida possuirá gráfico diferenciado
# também, use o sufixo [run]
#
#
# Ex.: 001-Fighter01 # gráfico em repouso
# 001-Fighter01[wlk] # gráfico em movimento
# 001-Fighter01[jmp] # gráfico em salto
# 001-Fighter01[run] # gráfico em corrida
#
# Se nenhum desses gráficos existir o gráfico normal será usado. Portanto
# se não quiser usar gráficos de movimento diferenciado basta omiti-los.
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# MUDAR POSE:
# Muda a pose atual do charset.
#
# Com os comandos "event_pose" e "player_pose" essa mudança é desativada
# automaticamente se o charset se mover. A animação não é automática,
# o charset so irá ter animação se a opção "Animação Parado" for ativada.
#
# Com os comandos "event_stance" e "player_stance" essa mudança não é
# desativada automaticamente, sendo necessário usar outro comando para
# "limpar" a pose.
#
# Com os comandos "event_animation" e "player_animation" animação é exibida
# automáticamente, independente do estado atual do charset. Se a animação
# possuir loop, a pose será anulada apenas se uma nova pose for usada.
#
# event_pose(ID, SUFIX, LOOP, LOCK, ANIM, MOV, FRAMES) # para eventos
# player_pose(ID, SUFIX, LOOP, LOCK, ANIM, MOV, FRAMES) # para heróis
# event_stance(ID, SUFIX, LOOP, LOCK, ANIM, MOV, FRAMES) # para eventos
# player_stance(ID, SUFIX, LOOP, LOCK, ANIM, MOV, FRAMES) # para heróis
# event_animation(ID, SUFIX, LOOP, LOCK, ANIM, MOV, FRAMES) # para eventos
# player_animation(ID, SUFIX, LOOP, LOCK, ANIM, MOV, FRAMES) # para heróis
#
# ID = ID do evento para eventos. Para personagens, se usado junto do
# script 'Legacy Engine - Caterpillar', representa o INDEX do personagem
# no grupo, sem o script de caterpillar deixe esse valor igual a Zero.
# SUFIX = sufixo do gráfico da pose, deve ser uma string
# Ex.: "[run]"
# "[damage]"
# LOOP = Definição de Repetição, true ou false, se false a animação é
# exibida apenas uma vez, retornando a pose de repouso automaticamente
# ao final da exibição.
# LOCK = Travar movimento, true ou false, se true, impede movimento do
# personagem enquanto a pose estiver sendo exibida.
# ANIM = Velocidade de animação. Valor numérico. Quanto maior, mais lento
# será a mudança de quadro. Se for igual a 0 ou omitido, a velocidade
# de mudança de quadro será baseada na velocidade de movimento atual do
# personagem.
# MOV = Ajuste na velocidade de movimento. Valor numérico positivo ou
# negativo. Este valor é adicionado à velocidade de movimento atual do
# personagem.
# FRAMES = Número de quadros de animação da pose. Valor numérico maior que
# zero. Pode ser usado caso você queria que uma pose especifica
# tenha menos quadros.
#
# Os valores podem ser omitidos, porém não podem ser "pulados". Se você deseja
# configurar o valor "ANIM", por exemplo, deve fornecer os valores "SFX",
# "LOOP" e "TRAVA"
#
# Ex.: player_animation(0, "[swd]", false, true)
# event_animation(1, "[jmp]", false, true, 10)
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# SEQUENCIA DE POSES:
# Para fazer o personagem exibir várias poses em sequencia use os comandos:
# player_sequence(ID, POSES)
# event_sequence(ID, POSES)
# ID = ID do evento para eventos. Para personagens, se usado junto do
# script 'Legacy Engine - Caterpillar', representa o INDEX do personagem
# no grupo, sem o script de caterpillar deixe esse valor igual a Zero.
# POSES = poses da sequencia, pode ser adicionadas quantas quiser.
# cada pose deve ser expressa da seguinte forma:
# {:sufix => "[sufixo]", :loop => true/false, :lock => true/false,
# :anim => numerico, :speed => numerico, :frame => numerico}
#
# :sufix = sufixo do gráfico da pose
# :loop = repetição da pose
# :lock = impedir movimento durante exibição
# :anim = velocidade de mudança de quadro da animação
# :speed = mudança na velocidade de movimento durante animação
# :frame = número de frames da animação
# Todos valores podem ser omitidos
# Ex.:
# pose1 = {:sufix => "[swd]", :lock => true}
# pose2 = {:sufix => "[axe]", :lock => true, :anim => 6}
# pose3 = {:sufix => "[shd]", :lock => false, :loop => true}
# player_sequence(0, pose1, pose2, pose3)
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# AJUSTE DE POSIÇÃO:
# Algumas vezes, por causa das animações, talvez seja necessário mudar
# a posição do sprite dentro do frame. Para que se possa fazer isso sem
# a prejudicar o alinhamento dos charsets, é possível adicionar valores
# de ajustes de posição ao gráfico do charset.
#
# Pode-se adicionar ao nome do gráfico o sufixo [x*] ou [y*] onde * é o
# ajuste em pixels, que pode ser positivo ou negativo.
#
# 001-Fighter01[y+32].png
# 001-Fighter01[x-16].png
# 001-Fighter01[x16][y16].png
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# IMPORTANTE: o sufixo de pose deve sempre ser a última coisa adicionada ao
# nome do gráfico.
# Se este script for usado junto do script 'Legacy Engine - Movimento Diagonal'
# o sufixo de pose deve vir após o sufixo de movimento diagonal.
# Já o sufixo de tamanho deve vir antes do sufixo de movimento diagonal.
#
# Ex.: 001-Fighter01[diag][wlk]
# Ex.: 001-Fighter01[x16][y16][diag][wlk]
#
#==============================================================================
#==============================================================================
# ** Legecy Engine
#------------------------------------------------------------------------------
# Módulo de Configuração do sistema Legacy Engine
#==============================================================================
$legacy_engine = {} if $legacy_engine.nil?
$legacy_engine["Animation Control"] = true
#==============================================================================
# ** Game_Character
#------------------------------------------------------------------------------
# Esta classe trata dos Heróis. Esta é usada como uma superclasse para as
# classes Game_Player e Game_Event.
#==============================================================================
class Game_Character
#--------------------------------------------------------------------------
# Variáveis Públicas
#--------------------------------------------------------------------------
attr_accessor :pose_sufix
attr_reader :playing_pose
attr_reader :pose_lock
#--------------------------------------------------------------------------
# Inicialização do Objeto
#--------------------------------------------------------------------------
alias initialize_animcontrol initialize
def initialize
initialize_animcontrol
@pose_sequence = []
@base_speed = @move_speed
change_pose
end
#--------------------------------------------------------------------------
# Atualização da animação
#--------------------------------------------------------------------------
def update_animation
@anime_count += 1.5 if @playing_pose
@direction = @lock_direction if @pose_lock
speed = @move_speed + @pose_speed
if (@anim_speed == 0 and @anime_count > 18 - speed * 2) or
(@anim_speed > 0 and @anime_count > @anim_speed)
if @stop_count > 0 and not @step_anime and not @playing_pose
@pattern = @original_pattern
else
frames = @character_name[/\[f(\d+)\]/i] ? $1.to_i : 4
@pattern = (@pattern + 1) % [frames, @pose_frames].min
end
change_pose if @pattern == 0 and not @pose_loop
@anime_count = 0
end
end
#--------------------------------------------------------------------------
# Atualização do Frame (Movimento)
#--------------------------------------------------------------------------
alias update_move_animcontrol update_move
def update_move
update_poses
update_move_animcontrol
end
#--------------------------------------------------------------------------
# Atualiação do Frame (Poses)
#--------------------------------------------------------------------------
def update_poses
return if @playing_pose or @playing_stance
change_pose("[wlk]", true, false) if @pose_sufix != "[wlk]" and not
($legacy_engine["Running"] and running?)
change_pose("[run]", true, false) if @pose_sufix != "[run]" and
$legacy_engine["Running"] and running?
end
#--------------------------------------------------------------------------
# Atualização do Frame (Pulo)
#--------------------------------------------------------------------------
alias update_jump_animcontrol update_jump
def update_jump
jumping_pose
update_jump_animcontrol
end
#--------------------------------------------------------------------------
# Pose de Pulo
#--------------------------------------------------------------------------
def jumping_pose
return if @playing_pose or @playing_stance
change_pose("[jmp]", true, false) if @pose_sufix != "[jmp]"
end
#--------------------------------------------------------------------------
# Atualização do Frame (Parada)
#--------------------------------------------------------------------------
alias update_stop_animcontrol update_stop
def update_stop
change_pose if ["[wlk]","[run]","[jmp]"].include?(@pose_sufix) and not
@playing_pose and not @playing_stance and
(@move_type == 0 or (@pattern == 0 and
@anime_count == 0 and not moving?))
update_stop_animcontrol
end
#--------------------------------------------------------------------------
# Atualização do movimento
#--------------------------------------------------------------------------
alias movement_update_animcontrol movement_update
def movement_update
return if @pose_lock
movement_update_animcontrol
end
#--------------------------------------------------------------------------
# Mudança de pose
#--------------------------------------------------------------------------
def update_pose(settings = default_settings.dup)
if settings == default_settings and not @pose_sequence.empty?
anim = @pose_sequence.shift
settings[:sufix] = anim[:sufix].nil? ? "" : anim[:sufix]
settings[:loop] = anim[:loop].nil? ? false : anim[:loop]
settings[:lock] = anim[:lock].nil? ? false : anim[:lock]
settings[:anim] = anim[:anim].nil? ? 0 : anim[:anim]
settings[:speed] = anim[:speed].nil? ? 0 : anim[:speed]
settings[:frame] = anim[:frame].nil? ? frames : anim[:frame]
@playing_pose = true
else
@pose_sequence.clear
end
@pose_sufix = settings[:sufix].downcase
@anim_speed = settings[:anim]
@pose_speed = settings[:speed]
@pose_frames = [settings[:frame], frames].min
@pose_loop = settings[:loop]
@pose_lock = (settings[:lock] and @playing_pose)
@lock_direction = @direction
@anime_count = 0 unless ["[wlk]","[run]","[jmp]"].include?(@pose_sufix)
@move_speed = @base_speed + @pose_speed
@pattern = 0
end
#--------------------------------------------------------------------------
# Definir valores padrão
#--------------------------------------------------------------------------
def default_settings
return {:sufix => "", :loop => true, :lock => false, :anim => 0,
:speed => 0, :frame => frames}
end
#--------------------------------------------------------------------------
# Mudança de pose
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def change_pose(sufix = "", loop = true, lock = false, anim = 0, speed = 0,
frame = frames, play = false, stance = false)
settings = {:sufix => sufix, :loop => loop, :lock => lock, :anim => anim,
:speed => speed, :frame => frame}
@playing_pose = play
@playing_stance = stance
update_pose(settings)
end
#--------------------------------------------------------------------------
# Mudança de estado
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def change_stance(sufix = "", loop = true, lock = false, anim = 0, speed = 0,
frame = frames)
change_pose(sufix, loop, lock, anim, speed, frame, false, true)
end
#--------------------------------------------------------------------------
# Exibir animação
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def play_pose(sufix = "", loop = true, lock = false, anim = 0, speed = 0,
frame = frames)
change_pose(sufix, loop, lock, anim, speed, frame, true)
end
#--------------------------------------------------------------------------
# Sequencia de animação
# poses : poses da sequencia
#--------------------------------------------------------------------------
def pose_sequence(poses)
@pose_sequence = poses
change_pose
end
#--------------------------------------------------------------------------
# Tipo de Movimento: Pré-Definido
#--------------------------------------------------------------------------
alias move_type_custom_animcontrol move_type_custom
def move_type_custom
move_type_custom_animcontrol
@base_speed = @move_speed
end
end
#==============================================================================
# ** Game_Event
#------------------------------------------------------------------------------
# Esta é a classe que engloba os eventos. O que inclui as funções nas páginas de
# evento alterando estas através das Condições de Evento, e faz funcionar os
# Processos Paralelos. Esta classe está inserida na classe Game_Map.
#==============================================================================
class Game_Event < Game_Character
#--------------------------------------------------------------------------
# Atualizar
#--------------------------------------------------------------------------
alias refresh_animcontrol refresh
def refresh
refresh_animcontrol
@base_speed = @move_speed
end
#--------------------------------------------------------------------------
# Determinar se está se Movendo
#--------------------------------------------------------------------------
def moving?
return (@pose_lock or super)
end
end
#==============================================================================
# ** Game_Player
#------------------------------------------------------------------------------
# Esta classe engloba o Jogador. Suas funções incluem a inicialização das
# determinantes dos eventos e o scroll do mapa. Se refere a "$game_player" para
# as instâncias desta classe.
#==============================================================================
class Game_Player < Game_Character
#--------------------------------------------------------------------------
# Determinar se está se Movendo
#--------------------------------------------------------------------------
def moving?
return (@pose_lock or super)
end
end
#==============================================================================
# ** Sprite_Character
#------------------------------------------------------------------------------
# Esta é o script que exibe os sprites dos Heróis. Levando em consideração
# a classe Game_Character fazendo correções quando necessário.
#==============================================================================
class Sprite_Character
#--------------------------------------------------------------------------
# Determinar atualização do gráfico do personagem
#--------------------------------------------------------------------------
alias update_character_animcontrol? update_character?
def update_character?
return true if @pose_sufix != @character.pose_sufix
return update_character_animcontrol?
end
#--------------------------------------------------------------------------
# Atualização das informações do personagem
#--------------------------------------------------------------------------
alias update_character_info_animcontrol update_character_info
def update_character_info
update_character_info_animcontrol
@pose_sufix = @character.pose_sufix
end
#--------------------------------------------------------------------------
# Determinar bitmap se for um personagem
#--------------------------------------------------------------------------
def set_character_bitmap
begin
begin
sufix = (direction % 2 == 1) ? "[diag]" : ""
name = @character_name + sufix + @pose_sufix
self.bitmap = RPG::Cache.character(name, @character_hue)
rescue
name = @character_name + @pose_sufix
self.bitmap = RPG::Cache.character(name, @character_hue)
end
rescue
self.bitmap = RPG::Cache.character(@character_name, @character_hue)
end
@cw = bitmap.width / @character.frames
@ch = bitmap.height / 4
self.ox = @cw / 2 - (@character_name[/\[x([\+\-]?\d+)\]/i] ? $1.to_i : 0)
self.oy = @ch - (@character_name[/\[y([\+\-]?\d+)\]/i] ? $1.to_i : 0)
end
end
#==============================================================================
# ** Interpreter
#------------------------------------------------------------------------------
# É a classe que interpreta os comandos de eventos do jogo.
# É usada dentro da classe Game_Event e Game_System.
#==============================================================================
class Interpreter
#--------------------------------------------------------------------------
# Mudar pose do jogador
# id : índice
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def player_pose(id = 0, sufix = "", loop = true, lock = false, anim = 0,
speed = 0, frame = nil)
if $legacy_engine["Caterpillar"] and id > 0
frame = $game_player.actors[index - 1].frames if frame.nil?
$game_player.actors[index - 1].change_pose(sufix, loop, lock, anim,
speed, frame)
else
frame = $game_player.frames if frame.nil?
$game_player.change_pose(sufix, loop, lock, anim, speed, frame)
end
end
#--------------------------------------------------------------------------
# Mudar pose do evento
# id : índice
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def event_stance(id = 0, sufix = "", loop = true, lock = false, anim = 0,
speed = 0, frame = nil)
return if $game_map.events[id].nil?
frame = $game_map.events[id].frames if frame.nil?
$game_map.events[id].change_stance(sufix, loop, lock, anim, speed, frame)
end
#--------------------------------------------------------------------------
# Mudar estado do jogador
# id : índice
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def player_stance(id = 0, sufix = "", loop = true, lock = false, anim = 0,
speed = 0, frame = nil)
if $legacy_engine["Caterpillar"] and id > 0
frame = $game_player.actors[index - 1].frames if frame.nil?
$game_player.actors[index - 1].change_pose(sufix, loop, lock, anim,
speed, frame)
else
frame = $game_player.frames if frame.nil?
$game_player.change_stance(sufix, loop, lock, anim, speed, frame)
end
end
#--------------------------------------------------------------------------
# Mudar estado do evento
# id : índice
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def event_pose(id = 0, sufix = "", loop = true, lock = false, anim = 0,
speed = 0, frame = nil)
return if $game_map.events[id].nil?
frame = $game_map.events[id].frames if frame.nil?
$game_map.events[id].change_pose(sufix, loop, lock, anim, speed, frame)
end
#--------------------------------------------------------------------------
# Exibir animação do jogador
# id : índice
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def player_animation(id = 0, sufix = "", loop = true, lock = false, anim = 0,
speed = 0, frame = nil)
if $legacy_engine["Caterpillar"] and id > 0
frame = $game_player.actors[index - 1].frames if frame.nil?
$game_player.actors[index - 1].play_pose(sufix, loop, lock, anim,
speed, frame)
else
frame = $game_player.frames if frame.nil?
$game_player.play_pose(sufix, loop, lock, anim, speed, frame)
end
end
#--------------------------------------------------------------------------
# Exibir animação do evento
# id : índice
# sufix : sufixo do gráfico da pose
# loop : repetição da pose
# lock : impedir movimento durante exibição
# anim : velocidade de mudança de quadro da animação
# speed : mudança na velocidade de movimento durante animação
# frame : número de frames da animação
#--------------------------------------------------------------------------
def event_animation(id = 0, sufix = "", loop = true, lock = false, anim = 0,
speed = 0, frame = nil)
return if $game_map.events[id].nil?
frame = $game_map.events[id].frames if frame.nil?
$game_map.events[id].play_pose(sufix, loop, lock, anim, speed, frame)
end
#--------------------------------------------------------------------------
# Exibir sequencia de animação do jogador
# id : índice
# poses : sequencia de poses
#--------------------------------------------------------------------------
def player_sequence(id = 0, *poses)
if $legacy_engine["Caterpillar"] and id > 0
frame = $game_player.actors[index - 1].frames if frame.nil?
$game_player.actors[index - 1].pose_sequence(poses)
else
$game_player.pose_sequence(poses)
end
end
#--------------------------------------------------------------------------
# Exibir sequencia de animação do evento
# id : índice
# poses : sequencia de poses
#--------------------------------------------------------------------------
def event_sequence(id = 0, *poses)
return if $game_map.events[id].nil?
$game_map.events[id].pose_sequence(poses)
end
end
Perguntas Frequentes
Pergunta: Fuincona no VX? Você pode fazer um para VX?Créditos e Agradecimentos
Resposta: Respectivamente: não e não.
Pergunta: Por que o nome 'Legacy Engine'?
Resposta:
Muitos dos sistemas que irei disponibilizar, inicialmente foram
projetados para meu projeto 'Legado da Existência'. Por isso decidi
nomea-lo com algo que lembre o nome do meu projeto (Para quem não sabe,
Legacy = Legado em inglês)
Pergunta: Por que o nome em inglês? Não é você que sempre reclama de estrangeirismo nos projetos?
Resposta: Eu pretendo divulgar esse sistema em comunidades estrangeiras. Simplismente por isso.
•Feito por Victor Sant
•Moghunter, pelos chars do XAS Hero que foram usados na demonstração