1 Lição 8.2 - Scenes - Nosso Primeiro Menu - 2º Parte 16/2/2012, 21:31
Fetelk13
Membro Honorário I
Quero iniciar esta aula agradecendo a todos pela paciência. Tenho estado com tantos projetos ao mesmo tempo que meu tempo livre acabou. Ser Torneiro Mecânico (minha profissão), Baixista (1º Hobby), Programador (2º,3º e 4º Hobbys) e Marido (Obrigação ;D) não é nada fácil...
Na aula anterior eu prometi que ia mostrar algo que muitos esperam, a muito tempo, que é a animação do heroi numa janela. Já vou adiantando que eu não sei se funciona com 4 ao mesmo tempo, eu sei como fazer de apenas um. Fizemos algumas alterações nas janelas do menu, hoje vamos construir uma janela que irá substituir a janela de Status. Vamos lá.
Criando uma janela com Barras de HP/SP e Animação
As barras não vamos criar, há vários códigos prontos para isso e eu não vejo necessidade de "recriarmos a roda". Para tanto, insira no projeto este código:
Neste código já há alguns complementos que usaremos depois. Inicie o código da janela, chamando-a de Window_Hero_Status. Ela terá uma clausula no procedimento initialize, o herói em questão. Como vamos fazer um menu para um único herói, vamos usar muito daqui para a frente a var $game_party.actors[0], que indica o primeiro herói da equipe. Sempre que for desenhar qualquer classe ou módulo, lembre-se de ir colocando os end, para evitar erros futuros. Inicie o código da janela assim:
No initialize, você com certeza já sabe o que é necessário para o funcionamento da janela. Crie uma janela com 320 X 192, e declare as seguintes vars:
@actor = actor -> O Herói em questão
@frame = 0 -> Var necessária para a animação
@pose = 0 -> Var necessária para a animação
E crie já um procedimento refresh. Se você já prestou bastante atenção nas outras aulas, já sabe que são os procedimentos necessários de uma janela. Até aqui, seu código deve estar assim:
Na sessão refresh, vamos desenhar o herói, nome, level, Status, Exp, HP e SP. Para isso, usaremos alguns dos procedimentos declarados como complementos da classe Window_Base. Vamos lá. Inicie o Procedimento refresh com um self.contents.clear, necessário, pois é ele quem permite a aualização da janela sem erros. Agora, usaremos alguns procedimentos usados na Window_MenuStatus. Dê uma boa olhada na imagem do menu anterior e, imagine quais procedimentos serão usados. Os nomes são óbvios, como draw_actor_hp sabendo que draw é desenhar, bem... ;D Para que você não se complique muito, aqui está o código:
O código de sua janela já deve estar assim:
Acho que não há nada pior que escrever muito e não testar... Vamos testar nossa janela substituindo-a pela Window_MenuStatus do menu. procure a seguinte linha no Scene_Menu:
@status_window = Window_MenuStatus.new
Aqui, declare assim:
@status_window = Window_Hero_Status.new($game_party.actors[0])
repare que já enviamos para a janela o parametro do herói. Execute o game e abra o menu, ele deve estar assim:
Ele ainda não tem o herói em movimento, por enquanto... Gerar uma animação, desde que você já tenha os quadros, é simples. No caso do Herói, é uma animação padrão, que o RMXP usa. Só vamos reproduzí-la. Lembra da aula passada quando falamos de Indexação?? Bem, se você pretende ser um bom programador, você verá essa palavra para o resto da sua vida... É ela que torna simples as tarefas que seriam complicadíssimas na linguagem. Vamos ver porque.
Declaramos duas vars no início do código, a @frame e a @pose. baseado na @frame, vamos "mover" o herói, e na pose vamos mudar sua direção. Observe a imagem abaixo:
Na horizontal, temos nossos frames, na vertical nossas poses. Levando em conta a indexação(que sempre começa no zero) vamos apenas "Substituir" o quadro do herói que será desenhado. Para isso, acrescente este procedimento na sua janela:
Se você observou bem esta matemática acima, já entendeu como funciona a "Animação". Mas eu vou explicar.
Quando usando a função Stretch_blt, explicada na apêndice sobre a classe Bitmap, ela desenha o bitmap no tamanho predefinido no rect, distorcendo se for mal calculada claro. Neste caso, eu fiz uma apliação mediana, pois as dimensões originais do char são de 32/48, aqui ele está sendo desenhado em 80/120. Agora a mágica vem aqui. Determinamos a área que será desenhada da imagem no Rect.new, usando uma fórmula bem simples:
bitmap.width / 4 * @frame
Aqui, eu digo ao Rect que eu quero que ele desenhe a imagem na posição X, usando a medida do bitmap dividida por 4 e multiplicada por @frame. Se você lembrar que qualquer valor multiplicado por 0 é 0, por 1 é ele mesmo, e assim vai, o rect muda de frame na direção Horizontal de acordo com a variação da var @frame. A mesma coisa acontece com a posição Y, e altura e largura não tem alterações. No procedimento refresh, adicione esta linha logo abaixo de self.contents.clear:
draw_actor_sprite
Experimente rodar seu projeto agora. O menu estará assim:
O herói ainda está estático. bem, a solução já vem. Você se lembra que na Scene há um loop infinito que mantém tudo atualizado no procedimento update? que todos os objetos declarados na Scene possuem um objeto.update dentro do procedimento? É aqui que faremos nossa animação funcionar. Dentro da janela, insira um procedimento update, assim:
O super significa que vamos apenas complementar o procedimento, o restante fica por conta da superclasse. Lembra das condições rápidas? aqui vamos usar a contagem de frames para, a cada determinado tempo, ele atualizar a imagem. assim:
MAS O QUE FIZEMOS AQUI ??
Toda vez que a contagem de frames radiciada por 10 retornar 0, a ação ocorrerá. Sim, é uma radiciação, é uma "Raiz". Porque isso? bem, se eu for explicar temos que abrir um tópico de aulas de matemática ... ;D O mais importante é que, toda vez que, na contagem de frames, existir o valor, ele efetue nossa condição. Se @frame for 3, ele a torna zero, se não, ele adiciona 1, e executa o draw_actor_sprite.
Depois de feito isso, rode seu game. MARAVILHOSO !!
Simples demais fazer uma animação. ( Meu pai sempre diz que depois que a onça tá morta qualquer um quer pegar no rabo dela ) ;D
Se quiser mudar a direção do herói, basta mudar o valor de @pose, entre 0 e 3.
UFA !! que dureza. Bem, esse foi mais um complemento do nosso menu. Ainda faltam 4 janelas, concertar funções, inserir o ABS... Bem, como eu sei que você se deu bem com este, o mais complexo, vou passar uma lição de casa... Observe a imagem abaixo, veja o que as outras janelas tem, as duas abaixo do herói. Uma delas mostra as armas, a outra os parâmetros. Todos estas funções você as encontra na Window_Status.
Sua lição será criar estas duas janelas, e inserí-las no menu.
Mudar a posição das janelas é fácil, já falamos sobre x e y e tenho certeza que pra você será simples. As outras( Nome do Mapa e Armas do SBAS) faremos na próxima aula.
Por enquanto é só.
Obrigado.
Na aula anterior eu prometi que ia mostrar algo que muitos esperam, a muito tempo, que é a animação do heroi numa janela. Já vou adiantando que eu não sei se funciona com 4 ao mesmo tempo, eu sei como fazer de apenas um. Fizemos algumas alterações nas janelas do menu, hoje vamos construir uma janela que irá substituir a janela de Status. Vamos lá.
Criando uma janela com Barras de HP/SP e Animação
As barras não vamos criar, há vários códigos prontos para isso e eu não vejo necessidade de "recriarmos a roda". Para tanto, insira no projeto este código:
- Código:
class Window_Base
def draw_actor_name2(actor, x, y,align=0)
self.contents.font.color = normal_color
self.contents.draw_text(x, y, 64, 32, actor.name ,align)
end
def draw_actor_parameter2(actor, x, y, type)
case type
when 0
parameter_name = $data_system.words.atk
parameter_value = actor.atk
when 1
parameter_name = $data_system.words.pdef
parameter_value = actor.pdef
when 2
parameter_name = $data_system.words.mdef
parameter_value = actor.mdef
when 3
parameter_name = $data_system.words.str
parameter_value = actor.str
when 4
parameter_name = $data_system.words.dex
parameter_value = actor.dex
when 5
parameter_name = $data_system.words.agi
parameter_value = actor.agi
when 6
parameter_name = $data_system.words.int
parameter_value = actor.int
end
self.contents.font.color = system_color
self.contents.draw_text(x, y, 120, 32, parameter_name)
self.contents.font.color = normal_color
self.contents.draw_text(x + 164, y, 36, 32, parameter_value.to_s, 2)
end
alias cbs_draw_actor_hp draw_actor_hp
def draw_actor_hp(actor, x, y, width = 146, height = 15)
bg = Color.new( 0, 0, 0, 160)
c1 = Color.new(255, 0, 0, 0)
c2 = Color.new(255, 255, 0, 160)
self.contents.fill_rect(x, y, width, height, bg)
width2 = width * actor.hp / actor.maxhp
gradient(x + 1, y + 1, width2 - 2, height - 2, c1, c2)
cbs_draw_actor_hp(actor, x, y, width)
end
alias cbs_draw_actor_sp draw_actor_sp
def draw_actor_sp(actor, x, y, width = 146, height = 15)
bg = Color.new( 0, 0, 0, 160)
c1 = Color.new( 0, 0, 255, 0)
c2 = Color.new( 0, 255, 255, 160)
self.contents.fill_rect(x, y, width, height, bg)
if actor.maxsp != 0
width2 = width * actor.sp / actor.maxsp
else
width2 = width * actor.sp / 1
end
gradient(x + 1, y + 1, width2 - 2, height - 2, c1, c2)
cbs_draw_actor_sp(actor, x, y, width)
end
def gradient(x, y, width, height, c1, c2)
for i in 1..width
x2 = x + i - 1
r = c1.red * (width - i) / width + c2.red * i / width
g = c1.green * (width - i) / width + c2.green * i / width
b = c1.blue * (width - i) / width + c2.blue * i / width
a = c1.alpha * (width - i) / width + c2.alpha * i / width
self.contents.fill_rect(x2, y, 1, height, Color.new(r, g, b, a))
end
end
end
Neste código já há alguns complementos que usaremos depois. Inicie o código da janela, chamando-a de Window_Hero_Status. Ela terá uma clausula no procedimento initialize, o herói em questão. Como vamos fazer um menu para um único herói, vamos usar muito daqui para a frente a var $game_party.actors[0], que indica o primeiro herói da equipe. Sempre que for desenhar qualquer classe ou módulo, lembre-se de ir colocando os end, para evitar erros futuros. Inicie o código da janela assim:
- Código:
class Window_Hero_Status < Window_Base
def initialize(actor)
end
end
No initialize, você com certeza já sabe o que é necessário para o funcionamento da janela. Crie uma janela com 320 X 192, e declare as seguintes vars:
@actor = actor -> O Herói em questão
@frame = 0 -> Var necessária para a animação
@pose = 0 -> Var necessária para a animação
E crie já um procedimento refresh. Se você já prestou bastante atenção nas outras aulas, já sabe que são os procedimentos necessários de uma janela. Até aqui, seu código deve estar assim:
- Código:
class Window_Hero_Status < Window_Base
def initialize(actor)
super(0, 0, 320, 192)
self.contents = Bitmap.new(width - 32, height - 32)
self.contents.font.name = $fontface
self.contents.font.size = $fontsize
@actor = actor
@frame = 0
@pose = 0
refresh
end
def refresh
end
end
Na sessão refresh, vamos desenhar o herói, nome, level, Status, Exp, HP e SP. Para isso, usaremos alguns dos procedimentos declarados como complementos da classe Window_Base. Vamos lá. Inicie o Procedimento refresh com um self.contents.clear, necessário, pois é ele quem permite a aualização da janela sem erros. Agora, usaremos alguns procedimentos usados na Window_MenuStatus. Dê uma boa olhada na imagem do menu anterior e, imagine quais procedimentos serão usados. Os nomes são óbvios, como draw_actor_hp sabendo que draw é desenhar, bem... ;D Para que você não se complique muito, aqui está o código:
- Código:
draw_actor_name2(@actor, 8, 0, 1)
draw_actor_class(@actor, 96, 0)
draw_actor_level(@actor, 96, 24)
draw_actor_state(@actor, 96, 48)
self.contents.font.color = system_color
self.contents.draw_text(96, 72, 80, 32, "EXP:")
self.contents.font.color = normal_color
self.contents.draw_text(96, 72, 160, 32, @actor.exp_s + "/" + @actor.next_exp_s , 2)
draw_actor_hp(@actor, 96, 100, 172)
draw_actor_sp(@actor, 96, 124, 172)
O código de sua janela já deve estar assim:
- Código:
class Window_Hero_Status < Window_Base
def initialize(actor)
super(0, 0, 320, 192)
self.contents = Bitmap.new(width - 32, height - 32)
self.contents.font.name = $fontface
self.contents.font.size = $fontsize
@actor = actor
@frame = 0
@pose = 0
refresh
end
def refresh
self.contents.clear
draw_actor_name2(@actor, 8, 0, 1)
draw_actor_class(@actor, 96, 0)
draw_actor_level(@actor, 96, 24)
draw_actor_state(@actor, 96, 48)
self.contents.font.color = system_color
self.contents.draw_text(96, 72, 80, 32, "EXP:")
self.contents.font.color = normal_color
self.contents.draw_text(96, 72, 160, 32, @actor.exp_s + "/" + @actor.next_exp_s , 2)
draw_actor_hp(@actor, 96, 100, 172)
draw_actor_sp(@actor, 96, 124, 172)
end
end
Acho que não há nada pior que escrever muito e não testar... Vamos testar nossa janela substituindo-a pela Window_MenuStatus do menu. procure a seguinte linha no Scene_Menu:
@status_window = Window_MenuStatus.new
Aqui, declare assim:
@status_window = Window_Hero_Status.new($game_party.actors[0])
repare que já enviamos para a janela o parametro do herói. Execute o game e abra o menu, ele deve estar assim:
Ele ainda não tem o herói em movimento, por enquanto... Gerar uma animação, desde que você já tenha os quadros, é simples. No caso do Herói, é uma animação padrão, que o RMXP usa. Só vamos reproduzí-la. Lembra da aula passada quando falamos de Indexação?? Bem, se você pretende ser um bom programador, você verá essa palavra para o resto da sua vida... É ela que torna simples as tarefas que seriam complicadíssimas na linguagem. Vamos ver porque.
Declaramos duas vars no início do código, a @frame e a @pose. baseado na @frame, vamos "mover" o herói, e na pose vamos mudar sua direção. Observe a imagem abaixo:
Na horizontal, temos nossos frames, na vertical nossas poses. Levando em conta a indexação(que sempre começa no zero) vamos apenas "Substituir" o quadro do herói que será desenhado. Para isso, acrescente este procedimento na sua janela:
- Código:
def draw_actor_sprite
#Limpa a área onde o heroi será desenhado
self.contents.fill_rect(0, 32, 80, 120, Color.new(0, 0, 0, 0))
#Carrega a imagem do herói numa var
bitmap = RPG::Cache.character(@actor.character_name, @actor.character_hue)
#desenha o herói, aumentado de tamanho e no frame correnspondente
self.contents.stretch_blt(Rect.new(0, 32, 80, 120), bitmap,
Rect.new(bitmap.width / 4 * @frame, bitmap.height / 4 * @pose , bitmap.width / 4 , bitmap.height / 4))
end
Se você observou bem esta matemática acima, já entendeu como funciona a "Animação". Mas eu vou explicar.
Quando usando a função Stretch_blt, explicada na apêndice sobre a classe Bitmap, ela desenha o bitmap no tamanho predefinido no rect, distorcendo se for mal calculada claro. Neste caso, eu fiz uma apliação mediana, pois as dimensões originais do char são de 32/48, aqui ele está sendo desenhado em 80/120. Agora a mágica vem aqui. Determinamos a área que será desenhada da imagem no Rect.new, usando uma fórmula bem simples:
bitmap.width / 4 * @frame
Aqui, eu digo ao Rect que eu quero que ele desenhe a imagem na posição X, usando a medida do bitmap dividida por 4 e multiplicada por @frame. Se você lembrar que qualquer valor multiplicado por 0 é 0, por 1 é ele mesmo, e assim vai, o rect muda de frame na direção Horizontal de acordo com a variação da var @frame. A mesma coisa acontece com a posição Y, e altura e largura não tem alterações. No procedimento refresh, adicione esta linha logo abaixo de self.contents.clear:
draw_actor_sprite
Experimente rodar seu projeto agora. O menu estará assim:
O herói ainda está estático. bem, a solução já vem. Você se lembra que na Scene há um loop infinito que mantém tudo atualizado no procedimento update? que todos os objetos declarados na Scene possuem um objeto.update dentro do procedimento? É aqui que faremos nossa animação funcionar. Dentro da janela, insira um procedimento update, assim:
- Código:
def update
super
end
O super significa que vamos apenas complementar o procedimento, o restante fica por conta da superclasse. Lembra das condições rápidas? aqui vamos usar a contagem de frames para, a cada determinado tempo, ele atualizar a imagem. assim:
- Código:
if Graphics.frame_count % 10 == 0
@frame == 3 ? @frame = 0 : @frame += 1
draw_actor_sprite
end
MAS O QUE FIZEMOS AQUI ??
Toda vez que a contagem de frames radiciada por 10 retornar 0, a ação ocorrerá. Sim, é uma radiciação, é uma "Raiz". Porque isso? bem, se eu for explicar temos que abrir um tópico de aulas de matemática ... ;D O mais importante é que, toda vez que, na contagem de frames, existir o valor, ele efetue nossa condição. Se @frame for 3, ele a torna zero, se não, ele adiciona 1, e executa o draw_actor_sprite.
Depois de feito isso, rode seu game. MARAVILHOSO !!
Simples demais fazer uma animação. ( Meu pai sempre diz que depois que a onça tá morta qualquer um quer pegar no rabo dela ) ;D
Se quiser mudar a direção do herói, basta mudar o valor de @pose, entre 0 e 3.
UFA !! que dureza. Bem, esse foi mais um complemento do nosso menu. Ainda faltam 4 janelas, concertar funções, inserir o ABS... Bem, como eu sei que você se deu bem com este, o mais complexo, vou passar uma lição de casa... Observe a imagem abaixo, veja o que as outras janelas tem, as duas abaixo do herói. Uma delas mostra as armas, a outra os parâmetros. Todos estas funções você as encontra na Window_Status.
Sua lição será criar estas duas janelas, e inserí-las no menu.
Mudar a posição das janelas é fácil, já falamos sobre x e y e tenho certeza que pra você será simples. As outras( Nome do Mapa e Armas do SBAS) faremos na próxima aula.
Por enquanto é só.
Obrigado.