Forums » RGSS

Lição 8.2 de RGSS - A Classe Scene - Continuação 2

    • 683 posts
    4 de junho de 2015 12h55min52s ART

    Quero iniciar esta aula agradecendo a todos pela paciência. 
    Tenho estado com tantos projetos ao mesmo tempo que meu tempo livre acabou. Ser Baixista(1º Hobby), Arquiteto de Soluções(Emprego) e Marido(Obrigação  Feliz) 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. é possível fazer para quantos chars quiser.
    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:

    classWindow_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     when0       parameter_name = $data_system.words.atk       parameter_value = actor.atk     when1       parameter_name = $data_system.words.pdef       parameter_value = actor.pdef     when2       parameter_name = $data_system.words.mdef       parameter_value = actor.mdef     when3       parameter_name = $data_system.words.str       parameter_value = actor.str     when4       parameter_name = $data_system.words.dex       parameter_value = actor.dex     when5       parameter_name = $data_system.words.agi       parameter_value = actor.agi     when6       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 in1..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   endend


    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:

    classWindow_Hero_Status<Window_Base   def initialize(actor)   endend

    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:

    classWindow_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     endend


    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...  Feliz Para que você não se complique muito, aqui está o 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:

    classWindow_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)   endend


    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:
    Imagem Postada

    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:
    Imagem Postada

    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:

    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:
    Imagem Postada

    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:

    def update     superend


    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:

    ifGraphics.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 ...  Feliz 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 )  Feliz
    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.

    Imagem Postada

    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.

    Obrigado.