Descrição: Aqui, vamos discutir como usar surfaces
Nível de dificuldade: Não sei. Me ajudem aí, nos comentários. Acho que é intermediário, mas tem coisas bem básicas.
Requerimentos: GM Pro, exemplos feitos com a versão 8
surfaces: Que bicho de 7 cabeças é esse tal de surfaces? Bicho nenhum. Surface é uma tela, só isso. Pense num paint brush, só que, ao invés de desenhar com o mouse, você vai desenhar com o teclado.
Você cria uma surface usando a função "surface_create(largura, altura)". Pronto, você criou uma surface. Ah, essa função te retorna a id da surface pra você usar depois, em outras funções, portanto armazene essa id numa variável. Vou chamar ela de "Surf", com "S" maiúsculo pro GM não sugerir autocompletar.
Código: Selecionar todos
"Surf=surface_create(largura, altura)".
Código: Selecionar todos
surface_set_target(Surf)
Ok, criamos, definimos que é nela que vamos desenhar e pintamos ela toda de preto. E agora? Agora, desenhe pra valer. Todas as funções de draw funcionam na surface, esteja você no evento em que estiver (sim, você pode desenhar fora do draw event).
Vai lá, desenha. Você pode desenhar sprites, linhas, triângulos (dá pra fazer qualquer coisa com um número suficiente de triângulos), círculos, outras surfaces, a própria surface, pode desenhar a tela, pode fazer o que quiser, desde que com código. Se você for bom de serviço, dá até pra usar uma interface com o usuário pra desenhar, mas isso foge do nosso assunto.
Desenhou pra valer? Ótimo. Agora chega. Depois você vai ao banheiro, estamos quase acabando, aqui.
Agora precisamos guardar essa tela. Em outras palavras, parar de desenhar na sua surface e voltar a desenhar na surface built-in que é a própria tela do jogo.
Código: Selecionar todos
surface_reset_target()
Código: Selecionar todos
draw_surface(Surf, x, y)
...
"Só isso?"
Bom, eu achei que já tava bom, mas já que é assim, vamos extender um pouco mais. Você está pensando "por que fazer a surface se eu posso fazer isso tudo direto na tela?", não é mesmo? Ok, deixa eu dar alguns exemplos do que se pode fazer com surfaces.
pausa - Quando você desativa todas as intâncias (instance_deactivate_all(1)), desativa todos os eventos delas, inclusive o evento de draw. Isso faz elas sumirem. Como fazer uma pausa que mostre a tela parada, sem ter que alterar o código de um-por-um dos objetos? Desative as instâncias mesmo mas, antes disso, crie uma surface com o evento de draw de todas elas. pra chamar o evento de draw de todas elas, não precisa usar with(all){event_perform(ev_draw, 0)}. Tem uma função ótima pra isso, "screen_redraw()". Crie a surface, desenhe tudo com screen_redraw(), desative todas as instâncias, desenhe a surface no draw event. Não, sério, é simples mesmo, olha só, coloque isso no obj_pausa:
evento de pausar
Código: Selecionar todos
if !pausa{
Surf=surface_create(room_width, room_height)
surface_set_target(Surf)
screen_redraw()
surface_reset_target()
instance_desctivte_all(1)
}
else{
instance_activate_all
surface_free(Surf) //apaga a surface, liberando memória
}
Código: Selecionar todos
if pausa
draw_surface(Surf, 0, 0)
Código: Selecionar todos
var i, Surf, xop, yop, xoc, yoc;
//armazena a origem dos sprites
xop=sprite_get_xoffset(spr_personagem)
yop=sprite_get_yoffset(spr_personagem)
xoc=sprite_get_xoffset(spr_capacete)
yoc=sprite_get_yoffset(spr_capacete)
Surf=surface_create(largura, altura)//largura e altura do sprite final
for(i=0; i<numero_de_imagens; i+=1){
draw_clear_alpha(0, 0) //aqui, você realmente quer eliminar o fundo
draw_sprite(spr_personagem, i, xop, yop) //desenha o personagem, sem nada
draw_sprite(spr_capacete, i, xoc, yoc) //desenha o capacete, por cima
if i=0{ //só pro primeiro índice
spr_vestido=sprite_create_from_surface(Surf, 0, 0, largura, altura, 0, 0, xop, yop,) //cria o sprite
}
else{ //se não é o primeiro índice, o sprite já existe
sprite_add_from_surface(spr_vestido, Surf, 0, 0, largura, altura, 0, 0) //só adicionar a imagem
}
}
surface_reset_target()//desliga a surface
surface_free(Surf) //não preciso mais da surface, elimino-a pra economizar memória.
screenshots - Quer publicar seu jogo e precisa de uma screenshot? Fácil! Em algum evento, crie uma surface do tamanho da room, desenhe a room e grave essa surface num arquivo de imagem! Olha só:
Código: Selecionar todos
var Surf;
Surf=surface_create(room_width, room_height)
surface_set_target(Surf)
draw_clear_alpha(0, 0)
screen_redraw()
surface_reset_target()
surface_save(Surf, "screenshot.png") //GM8
surface_free(Surf)
(na verdade, pode haver um problema em desenhar, no draw_event, uma surface que é criada usando screen_draw, porque você vai desenhar a própria surface. Mas isso não gera nenhum buraco no universo, só uma surface estranha).
Bom, é isso! Se eu não fui claro em algum aspecto ou exemplo, me avisem.
Abraço! Te vejo no tutorial de cores!
Edit: Lembra que eu falei que não é uma boa idéia desenhar a tela numa surface que será desenhada na mesma tela? Mantenho o que disse, mas fiquei com consciência pesada. Que tutorial é esse que fala do problema e não resolve?
[strike]Bom, acho que não preciso explicar porque não é uma boa idéia fazer isso.[/strike] Isso é uma má idéia porque a própria surface vai ser desenhada sobre ela mesma e, quando você faz isso, normalmente que fazer uma distorção na imagem, então essa distorção acaba sendo aumentada exponencialmente.
Mas o que fazer, então? Não sei. Sério, não sei mesmo. Mas tem um truque que pode ser interessante.
Tem outra coisa que já falei que pode ser uma boa dar uma relembrada. A tela que é mostrada durante o jogo também é uma surface. Você não precisa mandar desenhar nela porque o game maker está programado pra fazer isso. Mas você pode mandar parar de fazer. A função pra isso é
Código: Selecionar todos
set_automatic_draw(false)
Calma, não quer dizer que o jogo vai ficar sem imagem nenhuma. Quer dizer que, ao chegar no final do draw_event, o programa vai parar de desenhar a surface da tela por cima de tudo o que já tiver sido desenhado. Ou seja, vai passar a ser possível desenhar em qualquer evento. Como o screen_redraw só executa os eventos de draw e você NÃO vai desenhar essa surface no draw, ela não vai ser re-desenhada. Claro que isso pode gerar uma série de problemas (que eu não consigo imaginar agora), mas se for usado com cuidado, pode ficar bacana.
Olha como ficaria uma lupa:
create:
Código: Selecionar todos
//primeiro, faz a faca
faca=surface_create(50, 50) //os valores, você altera a gosto
surface_set_target(faca) //passa a desenhar na surface
draw_clear_alpha(c_white, 1) //deixa a surface toda branca
draw_set_blend_mode(bm_subtract) //passa a apagar, ao invés de desenhar
draw_circle_color(25, 25, 25, $ffffff, $ffffff, 0) //apaga um círculo no centro da surface
draw_set_blend_mode(bm_normal) //volta o blend_mode ao normal
surface_reset_target() //larga a surface quieta
Código: Selecionar todos
if mouse_check_button(mb_left){ //se o mouse está pressionado
set_automatic_draw(false) //para de redesenhar a tela
var Surf; //cria uma variável pra redesenhar tela
Surf=surface_create(room_width, room_height) //cria a surface que redesenhará a tela
surface_set_target(Surf) //passa a desenhar na surface
draw_clear_alpha(0, 0) //apaga tudo
screen_redraw() //desenha tudo
surface_reset_target() //para de desenhar na surface
lupa=surface_create(50, 50) //tem que ser do tamanho da faca
surface_set_target(lupa) //passa a desenhar na surface
draw_surface(Surf, -mouse_x, -mouse_y) //desenha a outra surface, adequadamenteposicionada
draw_set_blend_mode(bm_subtract) //passa a apagar ao invés de desenhar
draw_surface(sf_faca, 0, 0) //recorta, usando a faca
draw_set_blend_mode(bm_normal) //volta a desenhar ao invés de apagar
surface_reset_target() //para de desenhar na surface
draw_surface(Surf, 0, 0) //desenha a surface com a tela
draw_surface_stretched(lupa, mouse_x, mouse_y, 100, 100) //desenha a surface com a lupa, esticada
surface_free(Surf) //elimina a surface da tela
}else //se o mouse está solte
set_automatic_draw(true) //mantém a tela sendo desenhada normalmente